Skip to content

在 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是否可以被回收由属性**forceRecycleholder.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之前将属性动画的状态均重置即可。