AbsListView的重用机制——“存”方法分析

AbsListView的缓存行为主要由内部类RecycleBin的addScrapView方法实现

这是一个缺省修饰的方法 用于回收指定position的itemView(scrap)

/**
         * Puts a view into the list of scrap views.
         * <p>
         * If the list data hasn't changed or the adapter has stable IDs, views
         * with transient state will be preserved for later retrieval.

将视图放入废料视图列表中。
如果列表数据未更改或适配器具有稳定的ID,则将保留具有瞬态状态的视图以供以后检索。
         *
         * @param scrap The view to add
         * @param position The view's position within its parent
         */

    void addScrapView(View scrap, int position) {
            final AbsListView.LayoutParams lp = (AbsListView.LayoutParams) scrap.getLayoutParams();
            if (lp == null) {
                // Can't recycle, but we don't know anything about the view.
                // Ignore it completely.
                return;
            }

            lp.scrappedFromPosition = position;

            // Remove but don't scrap header or footer views, or views that
            // should otherwise not be recycled.
            final int viewType = lp.viewType;
            if (!shouldRecycleViewType(viewType)) {
                // Can't recycle. If it's not a header or footer, which have
                // special handling and should be ignored, then skip the scrap
                // heap and we'll fully detach the view later.
                if (viewType != ITEM_VIEW_TYPE_HEADER_OR_FOOTER) {
                    getSkippedScrap().add(scrap);
                }
                return;
            }

            scrap.dispatchStartTemporaryDetach();

            // The the accessibility state of the view may change while temporary
            // detached and we do not allow detached views to fire accessibility
            // events. So we are announcing that the subtree changed giving a chance
            // to clients holding on to a view in this subtree to refresh it.
            notifyViewAccessibilityStateChangedIfNeeded(
                    AccessibilityEvent.CONTENT_CHANGE_TYPE_SUBTREE);

            // Don't scrap views that have transient state.
            final boolean scrapHasTransientState = scrap.hasTransientState();
            if (scrapHasTransientState) {
                if (mAdapter != null && mAdapterHasStableIds) {
                    // If the adapter has stable IDs, we can reuse the view for
                    // the same data.
                    if (mTransientStateViewsById == null) {
                        mTransientStateViewsById = new LongSparseArray<>();
                    }
                    mTransientStateViewsById.put(lp.itemId, scrap);
                } else if (!mDataChanged) {
                    // If the data hasn't changed, we can reuse the views at
                    // their old positions.
                    if (mTransientStateViews == null) {
                        mTransientStateViews = new SparseArray<>();
                    }
                    mTransientStateViews.put(position, scrap);
                } else {
                    // Otherwise, we'll have to remove the view and start over.
                    getSkippedScrap().add(scrap);
                }
            } else {
                if (mViewTypeCount == 1) {
                    mCurrentScrap.add(scrap);
                } else {
                    mScrapViews[viewType].add(scrap);
                }

                if (mRecyclerListener != null) {
                    mRecyclerListener.onMovedToScrapHeap(scrap);
                }
            }
        }

方法里先判断  boolean scrapHasTransientState = scrap.hasTransientState(); 这里的View下的hasTransientState方法是个用于标记是否处于动画中的标记位方法,我们来看一下对应的set方法:


注释 /**
     * Set whether this view is currently tracking transient state that the
     * framework should attempt to preserve when possible. This flag is reference counted,
     * so every call to setHasTransientState(true) should be paired with a later call
     * to setHasTransientState(false).
     *
     * <p>A view with transient state cannot be trivially rebound from an external
     * data source, such as an adapter binding item views in a list. This may be
     * because the view is performing an animation, tracking user selection
     * of content, or similar.</p>
     *
     * @param hasTransientState true if this view has transient state
     */

设置此视图当前是否跟踪瞬时状态,该框架在可能时应尝试保存。这个标志是引用计数的,所以每一个电话,setHasTransientState(真正的)应该搭配之后调用setHasTransientState(假)。注意这里一定是成对出现的!!!
一种暂态观不能平凡反弹从外部数据源,如一个适配器绑定项目视图列表中。这可能是因为视图正在执行动画,跟踪用户选择内容,或类似的内容。
@param hastransientstate如果这种观点具有瞬态

再来看一下这个所谓的成对调用 到底跟动画有嘛关系?

豁然开朗~~~ 属性动画里的开始结束都拿这个标志位做控制


拉回主题,判断itemView完是否还有动画之后 回收策略分为两大分支:

先看有动画的:

    if (scrapHasTransientState) {
                if (mAdapter != null && mAdapterHasStableIds) {
                    // If the adapter has stable IDs, we can reuse the view for
                    // the same data.
                    if (mTransientStateViewsById == null) {
                        mTransientStateViewsById = new LongSparseArray<>();
                    }
                    mTransientStateViewsById.put(lp.itemId, scrap);
                } else if (!mDataChanged) {
                    // If the data hasn't changed, we can reuse the views at
                    // their old positions.
                    if (mTransientStateViews == null) {
                        mTransientStateViews = new SparseArray<>();
                    }
                    mTransientStateViews.put(position, scrap);
                } else {
                    // Otherwise, we'll have to remove the view and start over.
                    getSkippedScrap().add(scrap);
                }
            }
如果列表是stableIds的 就按id为key值存 在LongSparseArray类型的mTransientStateViewsById里

如果不是稳定id的就要掂量下这个数据还能不能用,这里用AbsListView的父类AdpterView中声明的列表是否数据更新了标记位mDataChanged做判断,如果!mDataChanged,即没改变,是新的可用数据,就按position为key值存在SparseArray类型的mTransientStateViews里。

查阅了一下能改变mDataChanged为true的地方有 :

notifyDataSetChanged -->observer.onChange

onFocusChanged

resetList

onSizeChanged

onAttachedToWindow

invalidateViews

setItemChecked

onRestoreInstanceState

否则mDataChanged为true,就只好废弃掉了

(注:这里源码没有直接detach并删除引用,而是将itemView加入到了一个如下列表里)

    private ArrayList<View> getSkippedScrap() {
            if (mSkippedScrap == null) {
                mSkippedScrap = new ArrayList<>();
            }
            return mSkippedScrap;
        }

这里不是特别理解,这么做有什么用。 事实上这个列表并没有再输出itemView的方法

RecycleBin也只提供了一个清空的方法

    /**
         * Finish the removal of any views that skipped the scrap heap.
         */
        void removeSkippedScrap() {
            if (mSkippedScrap == null) {
                return;
            }
            final int count = mSkippedScrap.size();
            for (int i = 0; i < count; i++) {
                removeDetachedView(mSkippedScrap.get(i), false);
            }
            mSkippedScrap.clear();
        }
实际上也就是等待被清空。


看完了有动画的分支 接下来就是无动画的回收处理

非常简单,viewType唯一的存ArrayList<View> mCurrentScrap里;不唯一的话也存在ArrayList<View>里 不过要按viewType下标放在mScrapViews数组里

    if (mViewTypeCount == 1) {
                    mCurrentScrap.add(scrap);
                } else {
                    mScrapViews[viewType].add(scrap);
                }

最后的最后

	if (mRecyclerListener != null) {
                    mRecyclerListener.onMovedToScrapHeap(scrap);
                }
如果有回收行为监听,通知回调


参考引用:ListView回收机制相关分析




  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值