AbsListView源码RecyclerBin解析

都知道ListView有复用View的机制,但是具体是怎么复用的却不怎么明白,里面的机制一概不知,百度了下也是云里雾里,果然纸上得来终觉浅,绝知此事要躬行,还是要自己深入源码去学习

RecycleBin的重要成员变量

		private RecyclerListener mRecyclerListener;
 //存储在mActiveViews中的第一个视图的位置。
        private int mFirstActivePosition;

        //可见的视图
        private View[] mActiveViews = new View[0];

        //适配器用来复用的废弃视图,也就是不可见的视图(缓存的)
        //嵌套的列表,下标对应类型
        private ArrayList<View>[] mScrapViews;
		//View的种类
        private int mViewTypeCount;
		//ViewType == 1时mScrapViews第一个元素值
        private ArrayList<View> mCurrentScrap;


        private ArrayList<View> mSkippedScrap;
        /*
        TransientState是用来标记VIew的属性,对应下面两种类型
        */
		//View对应的数据不变的TransientState的集合
        private SparseArray<View> mTransientStateViews;
        //View对应的控件Id不变的TransientState的View数组
        private LongSparseArray<View> mTransientStateViewsById;

RecycleBin的方法

对于RecycleBin无非就是存和取,在最开始的时候初始化,在最后清空,所以找到对应的方法便可了解其机制。
里面的方法如下:
在这里插入图片描述
根据方法名也很容易知道他的功能是什么来。先看看有没有set方法
setCacheColorHint()应该不是我们要找到,那还剩下setViewTypeCount():
果然这里进行了初始化的工作
setViewTypeCount:

public void setViewTypeCount(int viewTypeCount) {
            if (viewTypeCount < 1) {
                throw new IllegalArgumentException("Can't have a viewTypeCount < 1");
            }
            //根据设置的ViewType数目来初始化对应的ArrayList
            ArrayList<View>[] scrapViews = new ArrayList[viewTypeCount];
            //为每个ViewType创建一个存储不可见的View列表
            for (int i = 0; i < viewTypeCount; i++) {
                scrapViews[i] = new ArrayList<View>();
            }
            mViewTypeCount = viewTypeCount;
            mCurrentScrap = scrapViews[0];
            mScrapViews = scrapViews;
        }

再来看看存的操作
有个addScrapView()这名字太明显了,就是添加不可见的视图,大概就是滑动的时候,部分View从可见变为不可见,把这些添加以便复用,来看看代码。。。。篇幅很长
addScrapView:

//添加视图到ScrapView列表中,如果该ListView的data没有改变或者适配器有固定ID(对应上面两个成员变量),
//则添加到对应transientstate集合中
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;
            }
			//重新设置缓存ScrapView的位置,后面会用到
            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;
            //viewType > 0就会缓存
            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.
                //对不是顶部以及尾部的View进行缓存,应该很容易理解
                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.
            //不会去缓存含有transient state.标识的View
            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.
                    //如果该View具有固定的Id,可以复用数据
                    if (mTransientStateViewsById == null) {
                        mTransientStateViewsById = new LongSparseArray<>();
                    }
                    //存放进上面所说的TransientStateViews集合中
                    mTransientStateViewsById.put(lp.itemId, scrap);
                } else if (!mDataChanged) {
                    // If the data hasn't changed, we can reuse the views at
                    // their old positions.
                    //如果数据未改变,可以重复使用该View在原位置处
                    if (mTransientStateViews == null) {
                        mTransientStateViews = new SparseArray<>();
                    }
                    mTransientStateViews.put(position, scrap);
                } else {
                    // Otherwise, we'll have to remove the view and start over.
                    //其他情况放过过滤集合
                    clearScrapForRebind(scrap);
                    getSkippedScrap().add(scrap);
                }
            } else {
            //下面就是不含有TransientState标识的View
                clearScrapForRebind(scrap);
                if (mViewTypeCount == 1) {
                //只有一种类型就放人CurrentScrap列表中,与上面对应
                    mCurrentScrap.add(scrap);
                } else {
                    mScrapViews[viewType].add(scrap);
                }
				//这个是监听回收状态的监听器
                if (mRecyclerListener != null) {
                    mRecyclerListener.onMovedToScrapHeap(scrap);
                }
            }
        }

fillActiveViews

//将absListView的所有子项存放到可见视图数组中
void fillActiveViews(int childCount, int firstActivePosition) {
			//判断是否需要扩容
            if (mActiveViews.length < childCount) {
                mActiveViews = new View[childCount];
            }
            //更新第一个可见视图位置
            mFirstActivePosition = firstActivePosition;

            //noinspection MismatchedReadAndWriteOfArray
            final View[] activeViews = mActiveViews;
            for (int i = 0; i < childCount; i++) {
                View child = getChildAt(i);
                AbsListView.LayoutParams lp = (AbsListView.LayoutParams) child.getLayoutParams();
                // Don't put header or footer views into the scrap heap
                //将不是顶部和尾部的child赋值到activeViews数组
                if (lp != null && lp.viewType != ITEM_VIEW_TYPE_HEADER_OR_FOOTER) {
                    // Note:  We do place AdapterView.ITEM_VIEW_TYPE_IGNORE in active views.
                    //        However, we will NOT place them into scrap views.
                    activeViews[i] = child;
                    //纪录位置
                    lp.scrappedFromPosition = firstActivePosition + i;
                }
            }
        }

看完了存的操作下面来看怎么取出来这些View
getActiveView

//获取与指定位置对应的视图。 如果找到,视图将从mActiveViews中删除。
View getActiveView(int position) {
			//index就是当前位置-第一个可见视图位置
            int index = position - mFirstActivePosition;
            final View[] activeViews = mActiveViews;
            if (index >=0 && index < activeViews.length) {
                final View match = activeViews[index];
                //删除原数组中的值
                activeViews[index] = null;
                return match;
            }
            return null;
        }

getScrapView

//从缓存视图集合中获取指定位置的视图
View getScrapView(int position) {
			//判断当前位置的视图类型ViewType
            final int whichScrap = mAdapter.getItemViewType(position);
            if (whichScrap < 0) {
                return null;
            }
            //从ScrapView集合中获取
            if (mViewTypeCount == 1) {
                return retrieveFromScrap(mCurrentScrap, position);
            } else if (whichScrap < mScrapViews.length) {
                return retrieveFromScrap(mScrapViews[whichScrap], position);
            }
            return null;
        }

getTransientStateView

//根据position获取TransientStateView,获取后会移除相应的View,和上面的类似
View getTransientStateView(int position) {
            if (mAdapter != null && mAdapterHasStableIds && mTransientStateViewsById != null) {
                long id = mAdapter.getItemId(position);
                View result = mTransientStateViewsById.get(id);
                mTransientStateViewsById.remove(id);
                return result;
            }
            if (mTransientStateViews != null) {
                final int index = mTransientStateViews.indexOfKey(position);
                if (index >= 0) {
                    View result = mTransientStateViews.valueAt(index);
                    mTransientStateViews.removeAt(index);
                    return result;
                }
            }
            return null;
        }
//这个就是获取过滤数组,作用不大
private ArrayList<View> getSkippedScrap() {
            if (mSkippedScrap == null) {
                mSkippedScrap = new ArrayList<>();
            }
            return mSkippedScrap;
        }

下面来看清除操作

//用于清空所有的缓存视图数组
void clear() {
			//根据VIewType数目来清除
            if (mViewTypeCount == 1) {
                final ArrayList<View> scrap = mCurrentScrap;
                clearScrap(scrap);
            } else {
                final int typeCount = mViewTypeCount;
                for (int i = 0; i < typeCount; i++) {
                    final ArrayList<View> scrap = mScrapViews[i];
                    clearScrap(scrap);
                }
            }

            clearTransientStateViews();
        }

再来看看clearScrap()方法,都很容易理解

        private void clearScrap(final ArrayList<View> scrap) {
            final int scrapCount = scrap.size();
            for (int j = 0; j < scrapCount; j++) {
                removeDetachedView(scrap.remove(scrapCount - 1 - j), false);
            }
        }

还有个
clearTransientStateViews

//清空保存的TransientState标识的集合
void clearTransientStateViews() {
            final SparseArray<View> viewsByPos = mTransientStateViews;
            if (viewsByPos != null) {
                final int N = viewsByPos.size();
                for (int i = 0; i < N; i++) {
                    removeDetachedView(viewsByPos.valueAt(i), false);
                }
                viewsByPos.clear();
            }

            final LongSparseArray<View> viewsById = mTransientStateViewsById;
            if (viewsById != null) {
                final int N = viewsById.size();
                for (int i = 0; i < N; i++) {
                    removeDetachedView(viewsById.valueAt(i), false);
                }
                viewsById.clear();
            }
        }

fullyDetachScrapViews

//用来在ListView移除那些已经不可见的视图的关联,确保不可见视图列表中的任何剩余视图完全分离。
void fullyDetachScrapViews() {
            final int viewTypeCount = mViewTypeCount;
            final ArrayList<View>[] scrapViews = mScrapViews;
            for (int i = 0; i < viewTypeCount; ++i) {
                final ArrayList<View> scrapPile = scrapViews[i];
                for (int j = scrapPile.size() - 1; j >= 0; j--) {
                    final View view = scrapPile.get(j);
                    if (view.isTemporarilyDetached()) {
                        removeDetachedView(view, false);
                    }
                }
            }
        }

还有一些其他方法
markChildrenDirty

//用来重新调用缓存的child的forcelayout,forcelayout只会重新执行自己的onMeasure跟onLayout,不会调用父级的requestLayout()或forceLayout()。此方法会在ListView的size改变的时候进行调用。

public void markChildrenDirty() {
            if (mViewTypeCount == 1) {
                final ArrayList<View> scrap = mCurrentScrap;
                final int scrapCount = scrap.size();
                for (int i = 0; i < scrapCount; i++) {
                    scrap.get(i).forceLayout();
                }
            } else {
                final int typeCount = mViewTypeCount;
                for (int i = 0; i < typeCount; i++) {
                    final ArrayList<View> scrap = mScrapViews[i];
                    final int scrapCount = scrap.size();
                    for (int j = 0; j < scrapCount; j++) {
                        scrap.get(j).forceLayout();
                    }
                }
            }
            if (mTransientStateViews != null) {
                final int count = mTransientStateViews.size();
                for (int i = 0; i < count; i++) {
                    mTransientStateViews.valueAt(i).forceLayout();
                }
            }
            if (mTransientStateViewsById != null) {
                final int count = mTransientStateViewsById.size();
                for (int i = 0; i < count; i++) {
                    mTransientStateViewsById.valueAt(i).forceLayout();
                }
            }
        }

下面来看ListView使用缓存的流程,借用RecyclerView与ListView 对比浅析:缓存机制中的图片
在这里插入图片描述
参考:

深入理解Android中的缓存机制(二)RecyclerView跟ListView缓存机制对比
RecyclerView与ListView 对比浅析:缓存机制

发布了38 篇原创文章 · 获赞 6 · 访问量 3410

猜你喜欢

转载自blog.csdn.net/qq_37704124/article/details/98084154