天天记录 - Android ListView itemType使用Holder原理简单分析

来源:互联网 发布:ie浏览器不能上淘宝网 编辑:程序博客网 时间:2024/05/17 23:03


之前写过一篇关于Holder复用原理的文章《Android ListView使用Holder优化原理》,此篇着重分析RecycleBin数据结构


源码在AbsListView中,RecycleBin一共有两个存储结构分别是ActiveViews 和 ScrapViews

ActiveViews储存当前在界面(手机显示区域)中显示View,移出界面会存入ScrapViews
ScrapViews存储当前已经滑动出当前界面(手机显示区域)显示的View,这些view存储起来相当于回收,当再次请求的时候从此存储中取出反复使用


    /**     * 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     */    class RecycleBin {        private RecyclerListener mRecyclerListener;        /**         * The position of the first view stored in mActiveViews.         */        private int mFirstActivePosition;        /**         * Views that were on screen at the start of layout. This array is populated at the start of         * layout, and at the end of layout all view in mActiveViews are moved to mScrapViews.         * Views in mActiveViews represent a contiguous range of Views, with position of the first         * view store in mFirstActivePosition.         */        private View[] mActiveViews = new View[0];        /**         * Unsorted views that can be used by the adapter as a convert view.         */        private ArrayList<View>[] mScrapViews;        private int mViewTypeCount;        private ArrayList<View> mCurrentScrap;        private ArrayList<View> mSkippedScrap;        private SparseArray<View> mTransientStateViews;        public void setViewTypeCount(int viewTypeCount) {            if (viewTypeCount < 1) {                throw new IllegalArgumentException("Can't have a viewTypeCount < 1");            }            //noinspection unchecked            ArrayList<View>[] scrapViews = new ArrayList[viewTypeCount];            for (int i = 0; i < viewTypeCount; i++) {                scrapViews[i] = new ArrayList<View>();            }            mViewTypeCount = viewTypeCount;            mCurrentScrap = scrapViews[0];            mScrapViews = scrapViews;        }        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();                }            }        }        public boolean shouldRecycleViewType(int viewType) {            return viewType >= 0;        }        /**         * Clears the scrap heap.         */        void clear() {            if (mViewTypeCount == 1) {                final ArrayList<View> scrap = mCurrentScrap;                final int scrapCount = scrap.size();                for (int i = 0; i < scrapCount; i++) {                    removeDetachedView(scrap.remove(scrapCount - 1 - i), false);                }            } 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++) {                        removeDetachedView(scrap.remove(scrapCount - 1 - j), false);                    }                }            }            if (mTransientStateViews != null) {                mTransientStateViews.clear();            }        }        /**         * Fill ActiveViews with all of the children of the AbsListView.         *         * @param childCount The minimum number of views mActiveViews should hold         * @param firstActivePosition The position of the first view that will be stored in         *        mActiveViews         */        void fillActiveViews(int childCount, int firstActivePosition) {            if (mActiveViews.length < childCount) {                mActiveViews = new View[childCount];            }            mFirstActivePosition = firstActivePosition;            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                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;                }            }        }        /**         * 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;        }        View getTransientStateView(int position) {            if (mTransientStateViews == null) {                return null;            }            final int index = mTransientStateViews.indexOfKey(position);            if (index < 0) {                return null;            }            final View result = mTransientStateViews.valueAt(index);            mTransientStateViews.removeAt(index);            return result;        }        /**         * Dump any currently saved views with transient state.         */        void clearTransientStateViews() {            if (mTransientStateViews != null) {                mTransientStateViews.clear();            }        }        /**         * @return A view from the ScrapViews collection. These are unordered.         */        View getScrapView(int position) {            if (mViewTypeCount == 1) {                return retrieveFromScrap(mCurrentScrap, position);            } else {                int whichScrap = mAdapter.getItemViewType(position);                if (whichScrap >= 0 && whichScrap < mScrapViews.length) {                    return retrieveFromScrap(mScrapViews[whichScrap], position);                }            }            return null;        }        /**         * Put a view into the ScrapViews list. These views are unordered.         *         * @param scrap The view to add         */        void addScrapView(View scrap, int position) {            AbsListView.LayoutParams lp = (AbsListView.LayoutParams) scrap.getLayoutParams();            if (lp == null) {                return;            }            lp.scrappedFromPosition = position;            // Don't put header or footer views or views that should be ignored            // into the scrap heap            int viewType = lp.viewType;            final boolean scrapHasTransientState = scrap.hasTransientState();            if (!shouldRecycleViewType(viewType) || scrapHasTransientState) {                if (viewType != ITEM_VIEW_TYPE_HEADER_OR_FOOTER || scrapHasTransientState) {                    if (mSkippedScrap == null) {                        mSkippedScrap = new ArrayList<View>();                    }                    mSkippedScrap.add(scrap);                }                if (scrapHasTransientState) {                    if (mTransientStateViews == null) {                        mTransientStateViews = new SparseArray<View>();                    }                    scrap.dispatchStartTemporaryDetach();                    mTransientStateViews.put(position, scrap);                }                return;            }            scrap.dispatchStartTemporaryDetach();            if (mViewTypeCount == 1) {                mCurrentScrap.add(scrap);            } else {                mScrapViews[viewType].add(scrap);            }            scrap.setAccessibilityDelegate(null);            if (mRecyclerListener != null) {                mRecyclerListener.onMovedToScrapHeap(scrap);            }        }        /**         * 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();        }        /**         * Move all views remaining in mActiveViews to mScrapViews.         */        void scrapActiveViews() {            final View[] activeViews = mActiveViews;            final boolean hasListener = mRecyclerListener != null;            final boolean multipleScraps = mViewTypeCount > 1;            ArrayList<View> scrapViews = mCurrentScrap;            final int count = activeViews.length;            for (int i = count - 1; i >= 0; i--) {                final View victim = activeViews[i];                if (victim != null) {                    final AbsListView.LayoutParams lp                            = (AbsListView.LayoutParams) victim.getLayoutParams();                    int whichScrap = lp.viewType;                    activeViews[i] = null;                    final boolean scrapHasTransientState = victim.hasTransientState();                    if (!shouldRecycleViewType(whichScrap) || scrapHasTransientState) {                        // Do not move views that should be ignored                        if (whichScrap != ITEM_VIEW_TYPE_HEADER_OR_FOOTER ||                                scrapHasTransientState) {                            removeDetachedView(victim, false);                        }                        if (scrapHasTransientState) {                            if (mTransientStateViews == null) {                                mTransientStateViews = new SparseArray<View>();                            }                            mTransientStateViews.put(mFirstActivePosition + i, victim);                        }                        continue;                    }                    if (multipleScraps) {                        scrapViews = mScrapViews[whichScrap];                    }                    victim.dispatchStartTemporaryDetach();                    lp.scrappedFromPosition = mFirstActivePosition + i;                    scrapViews.add(victim);                    victim.setAccessibilityDelegate(null);                    if (hasListener) {                        mRecyclerListener.onMovedToScrapHeap(victim);                    }                }            }            pruneScrapViews();        }        /**         * Makes sure that the size of mScrapViews does not exceed the size of mActiveViews.         * (This can happen if an adapter does not recycle its views).         */        private void pruneScrapViews() {            final int maxViews = mActiveViews.length;            final int viewTypeCount = mViewTypeCount;            final ArrayList<View>[] scrapViews = mScrapViews;            for (int i = 0; i < viewTypeCount; ++i) {                final ArrayList<View> scrapPile = scrapViews[i];                int size = scrapPile.size();                final int extras = size - maxViews;                size--;                for (int j = 0; j < extras; j++) {                    removeDetachedView(scrapPile.remove(size--), false);                }            }            if (mTransientStateViews != null) {                for (int i = 0; i < mTransientStateViews.size(); i++) {                    final View v = mTransientStateViews.valueAt(i);                    if (!v.hasTransientState()) {                        mTransientStateViews.removeAt(i);                        i--;                    }                }            }        }        /**         * Puts all views in the scrap heap into the supplied list.         */        void reclaimScrapViews(List<View> views) {            if (mViewTypeCount == 1) {                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);                }            }        }        /**         * Updates the cache color hint of all known views.         *         * @param color The new cache color hint.         */        void setCacheColorHint(int color) {            if (mViewTypeCount == 1) {                final ArrayList<View> scrap = mCurrentScrap;                final int scrapCount = scrap.size();                for (int i = 0; i < scrapCount; i++) {                    scrap.get(i).setDrawingCacheBackgroundColor(color);                }            } 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).setDrawingCacheBackgroundColor(color);                    }                }            }            // Just in case this is called during a layout pass            final View[] activeViews = mActiveViews;            final int count = activeViews.length;            for (int i = 0; i < count; ++i) {                final View victim = activeViews[i];                if (victim != null) {                    victim.setDrawingCacheBackgroundColor(color);                }            }        }    }    static View retrieveFromScrap(ArrayList<View> scrapViews, int position) {        int size = scrapViews.size();        if (size > 0) {            // See if we still have a view for this position.            for (int i=0; i<size; i++) {                View view = scrapViews.get(i);                if (((AbsListView.LayoutParams)view.getLayoutParams())                        .scrappedFromPosition == position) {                    scrapViews.remove(i);                    return view;                }            }            return scrapViews.remove(size - 1);        } else {            return null;        }    }