在 viewholder离开显示区的时候会调用holder.isRecyclable()来判断viewholder是否可以被回收
java
void recycleViewHolderInternal(ViewHolder holder) {
final boolean transientStatePreventsRecycling = holder
.doesTransientStatePreventRecycling();
@SuppressWarnings("unchecked") final boolean forceRecycle = mAdapter != null
&& transientStatePreventsRecycling
&& mAdapter.onFailedToRecycleView(holder);
···
// 分析1 属性forceRecycle和holder.isRecyclable()的返回值共同决定
// viewholder是否可以被回收
// holder.isRecyclable() -> 分析2
if (forceRecycle || holder.isRecyclable()) {
if (mViewCacheMax > 0
&& !holder.hasAnyOfTheFlags(ViewHolder.FLAG_INVALID
| ViewHolder.FLAG_REMOVED
| ViewHolder.FLAG_UPDATE
| ViewHolder.FLAG_ADAPTER_POSITION_UNKNOWN)) {
// Retire oldest cached view
int cachedViewSize = mCachedViews.size();
if (cachedViewSize >= mViewCacheMax && cachedViewSize > 0) {
recycleCachedViewAt(0);
cachedViewSize--;
}
···
} else {
···
}
//分析2 判断view是否有临时状态
public final boolean isRecyclable() {
return (mFlags & FLAG_NOT_RECYCLABLE) == 0
&& !ViewCompat.hasTransientState(itemView);
//go to分析3
}
//分析3 可以追溯到view中的方法
//临时状态的返回值只要由两个标志位决定 -》分析4
public boolean hasTransientState() {
return (mPrivateFlags2 & PFLAG2_HAS_TRANSIENT_STATE)
== PFLAG2_HAS_TRANSIENT_STATE;
}
//分析4 主要有两个地方对transientState进行了写操作
//在ViewPropertyAnimator.java类中的startAnimation()方法设置了临时状态为true
//即当view开始做属性动画时,就会有transientState
private void startAnimation() {
mView.setHasTransientState(true);
ValueAnimator animator = ValueAnimator.ofFloat(1.0f);
ArrayList<NameValuesHolder> nameValueList =
(ArrayList<NameValuesHolder>) mPendingAnimations.clone();
mPendingAnimations.clear();
···
}
//在ViewPropertyAnimator.java类中的onAnimationEnd()方法设置了临时状态为false
//也就是View属性动画结束时就不会有transientState
public void onAnimationEnd(Animator animation) {
mView.setHasTransientState(false);
if (mAnimatorCleanupMap != null) {
Runnable r = mAnimatorCleanupMap.get(animation);
if (r != null) {
r.run();
}
mAnimatorCleanupMap.remove(animation);
}
```
}
因此当一个ViewHolder离开显示区域时有任意一个View在做属性动画,那么这个ViewHolder就没法被缓存到cacheView和RecylePool中。因此如果有循环动画那么可能就会导致ViewHolder回收失败 一个Viewholder是否可以被回收由属性**forceRecycle和holder.isRecyclable()的返回值共同决定 再回看forceRecycle属性是由什么决定的:**
java
void recycleViewHolderInternal(ViewHolder holder) {
final boolean transientStatePreventsRecycling = holder
.doesTransientStatePreventRecycling();
// 分析2 forceRecycle是一个boolean值,由多个条件 与 决定
// 其中 onFailedToRecycleView(holder)默认返回flase
// 因此 forceRecycle 默认是false
// onFailedToRecycleView(holder) -> 分析3
@SuppressWarnings("unchecked") final boolean forceRecycle = mAdapter != null
&& transientStatePreventsRecycling
&& mAdapter.onFailedToRecycleView(holder);
···
// 分析1 属性forceRecycle和holder.isRecyclable()的返回值共同决定
// viewholder是否可以被回收
// 属性forceRecycle -> 分析2
if (forceRecycle || holder.isRecyclable()) {
if (mViewCacheMax > 0
&& !holder.hasAnyOfTheFlags(ViewHolder.FLAG_INVALID
| ViewHolder.FLAG_REMOVED
| ViewHolder.FLAG_UPDATE
| ViewHolder.FLAG_ADAPTER_POSITION_UNKNOWN)) {
// Retire oldest cached view
int cachedViewSize = mCachedViews.size();
if (cachedViewSize >= mViewCacheMax && cachedViewSize > 0) {
recycleCachedViewAt(0);
cachedViewSize--;
}
···
} else {
···
}
// 分析3 RecyclerView.java
// 默认返回值为false
public boolean onFailedToRecycleView(@NonNull VH holder) {
return false;
}
因此如果需要回收掉由循环动画的View,可以重写**_onFailedToRecycleView()_**
方法,让其默认返回true,这样**forceRecycle**属性就会为true。
java
public boolean onFailedToRecycleView(@NonNull VH holder) {
return true;
}
为什么要设计成View在播放属性动画是默认不能被回收
因为避免在复用ViewHolder时,之前的属性动画的状态被保留。为了能解决能回收带有属性动画的ViewHolder的同时,又避免再复用的时候ViewHolder保存着之前的动画状态,我们可以在回收ViewHolder之前将属性动画的状态均重置即可。