Android源码学习之ListView的复用回收机制剖析.

来源:互联网 发布:淘宝推广计划怎么写 编辑:程序博客网 时间:2024/06/06 21:39

本博客参考了地址:点击打开链接

在刚开始接触学习Android基础的时候,ListView算是一个比较神奇的控件了,因为那时候好多效果都可以用它实现,而且用它就得用到一个设计模式,[适配器].结果昨天遗留下来一个bug,带这个解决这个Bug去翻看了5.0.1 API22的ListView部分源码分析复用.

复用到底有什么用.?简单的举个例子,假如你想要展示一万条item,作为手机不可能一下将一万条同时加载进去,这样肯定会OOM的,所以Google开发者想到了复用,也算是ListView高级的一个特点.

竟然复用的作用明白了,那Android到底是怎么复用的啊,?先看看ListView的结构图.

可以看到ScrollView,ListView,ExpanableListView,GridView都是继承于ViewGroup,说到这儿我想起一个隐藏的CallBack:OverScrollBy();

该方法算是Google隐藏起来了的,应该是为了模仿IOS的回弹阻尼效果,结果....

不过通过该方法还是能很轻易的实现,当然在新版本的RecycleView并不会回调该方法了.

关系中复用的核心类主要是放在了:AbsListView中的RecycleBin类中.该类源码中也详细说了两个重要的对象:ActiveViews,ScrapViews.先看说明然后再分析两个对象.

/**     * The RecycleBin facilitates reuse of views across layouts. The RecycleBin has two levels of     * storage: ActiveViews and ScrapViews. ActiveViews are those views which were onscreen at the     * start of a layout. By construction, they are displaying current information. At the end of     * layout, all views in ActiveViews are demoted to ScrapViews. ScrapViews are old views that     * could potentially be used by the adapter to avoid allocating views unnecessarily.     *     * @see android.widget.AbsListView#setRecyclerListener(android.widget.AbsListView.RecyclerListener)     * @see android.widget.AbsListView.RecyclerListener     */
ActiveViews顾名思义:当前活动,什么叫当前活动的,就是当前屏幕上可视的VIew,并且第一次被创建.

ScrapViews:被废弃回收的视图,就是指当前手指滑出屏幕外的被回收的视图.

可以看出来这两个对象非常重要,该类中的一些方法:

/**         * Puts all views in the scrap heap into the supplied list.         */        void reclaimScrapViews(List<View> views) {            if (mViewTypeCount == 1) {  // 这的Type是默认的,表示说如果你的Item只有一种类型,就只有一个集合回收,                views.addAll(mCurrentScrap);//添加到回收            } else { // 如果是多个类型就要创建多个不同类型的回收集合.                final int viewTypeCount = mViewTypeCount;                final ArrayList<View>[] scrapViews = mScrapViews;                for (int i = 0; i < viewTypeCount; ++i) {                    final ArrayList<View> scrapPile = scrapViews[i];                    views.addAll(scrapPile);                }            }        }

/**     * Returns the height of the view for the specified position.     *     * @param position the item position     * @return view height in pixels     */    int getHeightForPosition(int position) {        final int firstVisiblePosition = getFirstVisiblePosition();        final int childCount = getChildCount();        final int index = position - firstVisiblePosition; // 计算索引        if (index >= 0 && index < childCount) { // 在Childs范围内            // Position is on-screen, use existing view.            final View view = getChildAt(index);// 直接查找            return view.getHeight();        } else {            // Position is off-screen, obtain & recycle view.            final View view = obtainView(position, mIsScrap); // 超出屏幕外,Obtain,            view.measure(mWidthMeasureSpec, MeasureSpec.UNSPECIFIED);//测量子孩子.            final int height = view.getMeasuredHeight();            mRecycler.addScrapView(view, position); // 添加到废弃的Views中,            return height;        }    }


 /**     * Scroll the children by amount, adding a view at the end and removing     * views that fall off as necessary.     *     * @param amount The amount (positive or negative) to scroll.     */    private void scrollListItemsBy(int amount) {        offsetChildrenTopAndBottom(amount);        final int listBottom = getHeight() - mListPadding.bottom;//底部位置        final int listTop = mListPadding.top;// 顶部位置        final AbsListView.RecycleBin recycleBin = mRecycler;        if (amount < 0) {            // shifted items up            // may need to pan views into the bottom space            int numChildren = getChildCount();            View last = getChildAt(numChildren - 1); // 获取最后一个item            while (last.getBottom() < listBottom) {// 最后一个Item的底部比较.                final int lastVisiblePosition = mFirstPosition + numChildren - 1;                if (lastVisiblePosition < mItemCount - 1) {                    last = addViewBelow(last, lastVisiblePosition);//将新的View添加到下面,                    numChildren++;                } else {                    break;                }            }            // may have brought in the last child of the list that is skinnier            // than the fading edge, thereby leaving space at the end.  need            // to shift back            if (last.getBottom() < listBottom) {                offsetChildrenTopAndBottom(listBottom - last.getBottom());            }            // top views may be panned off screen            View first = getChildAt(0);            while (first.getBottom() < listTop) {//顶部的View的比较                AbsListView.LayoutParams layoutParams = (LayoutParams) first.getLayoutParams();                if (recycleBin.shouldRecycleViewType(layoutParams.viewType)) {                    recycleBin.addScrapView(first, mFirstPosition); // 顶部是直接添加的了回收对象集合中去了.                }                detachViewFromParent(first);//并从ViewGroup集合中移除该View,这的移除待会分析.                first = getChildAt(0); // 重新获取第一个位置循环,这里的第一个已经是下一个的意思了.                mFirstPosition++;            }        } else { // 下滑            // shifted items down            View first = getChildAt(0);            // may need to pan views into top            while ((first.getTop() > listTop) && (mFirstPosition > 0)) {                first = addViewAbove(first, mFirstPosition);                mFirstPosition--;            }            // may have brought the very first child of the list in too far and            // need to shift it back            if (first.getTop() > listTop) {                offsetChildrenTopAndBottom(listTop - first.getTop());            }            int lastIndex = getChildCount() - 1;            View last = getChildAt(lastIndex);            // bottom view may be panned off screen            while (last.getTop() > listBottom) {                AbsListView.LayoutParams layoutParams = (LayoutParams) last.getLayoutParams();                if (recycleBin.shouldRecycleViewType(layoutParams.viewType)) {                    recycleBin.addScrapView(last, mFirstPosition+lastIndex);                }                detachViewFromParent(last);                last = getChildAt(--lastIndex);            }        }    }


// This method also sets the child's mParent to null    private void removeFromArray(int index) {        final View[] children = mChildren;        if (!(mTransitioningViews != null && mTransitioningViews.contains(children[index]))) {            children[index].mParent = null; // 并未直接Remove,等待GC去回收.        }        final int count = mChildrenCount;        if (index == count - 1) {            children[--mChildrenCount] = null;        } else if (index >= 0 && index < count) {            System.arraycopy(children, index + 1, children, index, count - index - 1);            children[--mChildrenCount] = null;        } else {            throw new IndexOutOfBoundsException();        }        if (mLastTouchDownIndex == index) {            mLastTouchDownTime = 0;            mLastTouchDownIndex = -1;        } else if (mLastTouchDownIndex > index) {            mLastTouchDownIndex--;        }    }

所以添加到ScrapViews中后,该View仅仅是处于游离状态.

ListView可以设置setRecyclerListener,该接口会调用到RecycleBin中的reclaimView中,也是上面分析,

@Override    public void onMovedToScrapHeap(View view) {        //view是Item的Viewgroup.    }


参考Demo:http://blog.csdn.net/u010316858/article/details/47440841


0 0