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

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

在刚开始接触学习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
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值