ListView源码解析

本文深入解析了ListView的RecycleBin机制,包括mActiveViews、mCurrentScrap等关键变量的作用,以及addScrapView、getScrapView等核心方法的功能。通过理解这一机制,可以更好地掌握ListView的内存优化和视图复用策略。
摘要由CSDN通过智能技术生成

读过的链接

RecycleBin机制

变量

这里写图片描述
1. mActiveViews:存储第一次显示在屏幕上的View,所有的Active View最终都会被移动到Scrap View中
2. mCurrentScrapArrayList<View>[],跟mScrapViews的却别是,mScrapViews是个队列数组,ArrayList<View>[]类型,数组长度为mViewTypeCount,而默认ViewTypeCount = 1的情况下mCurrentScrap=mScrapViews[0]。
3. mFirstActivePosition:存储在mActiveViews中的第一个view的位置,即getFirstVisiblePosition。
4. mRecyclerListener

   public static interface RecyclerListener {
        /**
         * Indicates that the specified View was moved into the recycler's scrap heap.
         * The view is not displayed on screen any more and any expensive resource
         * associated with the view should be discarded.
         *
         * @param view
         */
        void onMovedToScrapHeap(View view);
    }

当发生View回收时,mRecyclerListener若有注册,则会通知给注册者.RecyclerListener接口只有一个函数onMovedToScrapHeap,指明某个view被回收到了scrap heap. 该view不再被显示,任何相关的昂贵资源应该被丢弃。该函数是处理回收时view中的资源释放。
5. mScrapViewsArrayList<View>[]存储被Adapter复用的View,注意:这里是一个数组,因为如果adapter中数据有多种类型,那么就会有多个ScrapViews。
6. mSkippedScrap:(翻译:跳过废弃)
7. mTransientStateViews:(翻译:瞬态状态视图)
8. mTransientStateViewsById
9. mViewTypeCount:View类型总数,列表中可能有多种数据类型,比如内容数据和分割符。

方法

这里写图片描述

  1. addScrapView(View,int):将view放入scrapview list中,放入时位置赋给scrappedFromPosition 。有transient状态的view不会被scrap(废弃),会被加入mSkippedScrap。就是将移出可视区域的view,设置它的scrappedFromPosition,然后从窗口中detach该view,并根据viewType加入到mScrapView中。
  /**
     * 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.
     *
     * @param scrap 要添加的view
     * @param position view在父类中的位置
     */
    void addScrapView(View scrap, int position) {
        final AbsListView.LayoutParams lp = (AbsListView.LayoutParams) scrap.getLayoutParams();
        if (lp == null) {
            return;
        }

        lp.scrappedFromPosition = position;

        // header view和footer view不被废弃
        final int viewType = lp.viewType;
        if (!shouldRecycleViewType(viewType)) {
            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);

        // 有transient状态的view不会被scrap(废弃)
        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<View>();
                }
                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<View>();
                }
                mTransientStateViews.put(position, scrap);
            } else {
                // Otherwise, we'll have to remove the view and start over.
                if (mSkippedScrap == null) {
                    mSkippedScrap = new ArrayList<View>();
                }
                mSkippedScrap.add(scrap);
            }
        } else {
            if (mViewTypeCount == 1) {
                mCurrentScrap.add(scrap);
            } else {
                mScrapViews[viewType].add(scrap);
            }

            if (mRecyclerListener != null) {
                mRecyclerListener.onMovedToScrapHeap(scrap);
            }
        }
    }
  1. getScrapView(int):
 /**
     * @return A view from the ScrapViews collection. These are unordered.
     */
    View getScrapView(int position) {
        if (mViewTypeCount == 1) {
            return retrieveFromScrap(mCurrentScrap, position);
        } else {
            final int whichScrap = mAdapter.getItemViewType(position);
            if (whichScrap >= 0 && whichScrap < mScrapViews.length) {
                return retrieveFromScrap(mScrapViews[whichScrap], position);
            }
        }
        return null;
    }

3.clear()空废弃view堆,并将这些View从窗口中Detach。

 /**
     * 空废弃view堆,并将这些View从窗口中Detach。
     */
    void clear() {
        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();
    }

4.fillActiveViews(int,int):将当前AbsListView的0-childCount个子类中的非header、footer类添加到mActiveViews数组中

  /**
     * 用AbsListView.的所有子view填充ActiveViews.
     *
     * @param childCount mActiveViews应该保存的最少的view数
     * @param mActiveViews中存储的首个view的位置
     */
    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();
            // 不放header view和footer view
            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;
                // Remember the position so that setupChild() doesn't reset state.
                lp.scrappedFromPosition = firstActivePosition + i;
            }
        }
    }

5.getActiveView(View):获取mActiveViews中指定位置的view,如果找到会将该view从mActiveViews中移除。


    /**
     * Get the view corresponding to the specified position. The view will be removed from
     * mActiveViews if it is found.
     *
     * @param position The position to look up in mActiveViews
     * @return The view if it is found, null otherwise
     */
    View getActiveView(int position) {
        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;
    }
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值