美文网首页
2019-10-26

2019-10-26

作者: xiaoluo | 来源:发表于2019-10-26 12:37 被阅读0次
    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;
            private LongSparseArray<View> mTransientStateViewsById;
    
            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();
                    }
                }
                if (mTransientStateViewsById != null) {
                    final int count = mTransientStateViewsById.size();
                    for (int i = 0; i < count; i++) {
                        mTransientStateViewsById.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;
                    clearScrap(scrap);
                } else {
                    final int typeCount = mViewTypeCount;
                    for (int i = 0; i < typeCount; i++) {
                        final ArrayList<View> scrap = mScrapViews[i];
                        clearScrap(scrap);
                    }
                }
    
                clearTransientStateViews();
            }
    
            /**
             * 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;
    
                //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
                    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;
                    }
                }
            }
    
            /**
             * 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 (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;
            }
    
            /**
             * Dumps and fully detaches any currently saved views with transient
             * state.
             */
            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();
                }
            }
    
            /**
             * @return A view from the ScrapViews collection. These are unordered.
             */
            View getScrapView(int position) {
                final int whichScrap = mAdapter.getItemViewType(position);
                if (whichScrap < 0) {
                    return null;
                }
                if (mViewTypeCount == 1) {
                    return retrieveFromScrap(mCurrentScrap, position);
                } else if (whichScrap < mScrapViews.length) {
                    return retrieveFromScrap(mScrapViews[whichScrap], position);
                }
                return null;
            }
    
            /**
             * 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 The view to add
             * @param position The view's position within its parent
             */
            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;
                }
    
                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;
                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.
                    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.
                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<>();
                        }
                        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<>();
                        }
                        mTransientStateViews.put(position, scrap);
                    } else {
                        // Otherwise, we'll have to remove the view and start over.
                        clearScrapForRebind(scrap);
                        getSkippedScrap().add(scrap);
                    }
                } else {
                    clearScrapForRebind(scrap);
                    if (mViewTypeCount == 1) {
                        mCurrentScrap.add(scrap);
                    } else {
                        mScrapViews[viewType].add(scrap);
                    }
    
                    if (mRecyclerListener != null) {
                        mRecyclerListener.onMovedToScrapHeap(scrap);
                    }
                }
            }
    
            private ArrayList<View> getSkippedScrap() {
                if (mSkippedScrap == null) {
                    mSkippedScrap = new ArrayList<>();
                }
                return mSkippedScrap;
            }
    
            /**
             * 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();
                        final int whichScrap = lp.viewType;
    
                        activeViews[i] = null;
    
                        if (victim.hasTransientState()) {
                            // Store views with transient state for later use.
                            victim.dispatchStartTemporaryDetach();
    
                            if (mAdapter != null && mAdapterHasStableIds) {
                                if (mTransientStateViewsById == null) {
                                    mTransientStateViewsById = new LongSparseArray<View>();
                                }
                                long id = mAdapter.getItemId(mFirstActivePosition + i);
                                mTransientStateViewsById.put(id, victim);
                            } else if (!mDataChanged) {
                                if (mTransientStateViews == null) {
                                    mTransientStateViews = new SparseArray<View>();
                                }
                                mTransientStateViews.put(mFirstActivePosition + i, victim);
                            } else if (whichScrap != ITEM_VIEW_TYPE_HEADER_OR_FOOTER) {
                                // The data has changed, we can't keep this view.
                                removeDetachedView(victim, false);
                            }
                        } else if (!shouldRecycleViewType(whichScrap)) {
                            // Discard non-recyclable views except headers/footers.
                            if (whichScrap != ITEM_VIEW_TYPE_HEADER_OR_FOOTER) {
                                removeDetachedView(victim, false);
                            }
                        } else {
                            // Store everything else on the appropriate scrap heap.
                            if (multipleScraps) {
                                scrapViews = mScrapViews[whichScrap];
                            }
    
                            lp.scrappedFromPosition = mFirstActivePosition + i;
                            removeDetachedView(victim, false);
                            scrapViews.add(victim);
    
                            if (hasListener) {
                                mRecyclerListener.onMovedToScrapHeap(victim);
                            }
                        }
                    }
                }
                pruneScrapViews();
            }
    
            /**
             * At the end of a layout pass, all temp detached views should either be re-attached or
             * completely detached. This method ensures that any remaining view in the scrap list is
             * fully detached.
             */
            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);
                        }
                    }
                }
            }
    
            /**
             * Makes sure that the size of mScrapViews does not exceed the size of
             * mActiveViews, which can happen if an adapter does not recycle its
             * views. Removes cached transient state views that no longer have
             * transient state.
             */
            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();
                    while (size > maxViews) {
                        scrapPile.remove(--size);
                    }
                }
    
                final SparseArray<View> transViewsByPos = mTransientStateViews;
                if (transViewsByPos != null) {
                    for (int i = 0; i < transViewsByPos.size(); i++) {
                        final View v = transViewsByPos.valueAt(i);
                        if (!v.hasTransientState()) {
                            removeDetachedView(v, false);
                            transViewsByPos.removeAt(i);
                            i--;
                        }
                    }
                }
    
                final LongSparseArray<View> transViewsById = mTransientStateViewsById;
                if (transViewsById != null) {
                    for (int i = 0; i < transViewsById.size(); i++) {
                        final View v = transViewsById.valueAt(i);
                        if (!v.hasTransientState()) {
                            removeDetachedView(v, false);
                            transViewsById.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);
                    }
                }
            }
    
            private View retrieveFromScrap(ArrayList<View> scrapViews, int position) {
                final int size = scrapViews.size();
                if (size > 0) {
                    // See if we still have a view for this position or ID.
                    // Traverse backwards to find the most recently used scrap view
                    for (int i = size - 1; i >= 0; i--) {
                        final View view = scrapViews.get(i);
                        final AbsListView.LayoutParams params =
                                (AbsListView.LayoutParams) view.getLayoutParams();
    
                        if (mAdapterHasStableIds) {
                            final long id = mAdapter.getItemId(position);
                            if (id == params.itemId) {
                                return scrapViews.remove(i);
                            }
                        } else if (params.scrappedFromPosition == position) {
                            final View scrap = scrapViews.remove(i);
                            clearScrapForRebind(scrap);
                            return scrap;
                        }
                    }
                    final View scrap = scrapViews.remove(size - 1);
                    clearScrapForRebind(scrap);
                    return scrap;
                } else {
                    return null;
                }
            }
    
            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);
                }
            }
    
            private void clearScrapForRebind(View view) {
                view.clearAccessibilityFocus();
                view.setAccessibilityDelegate(null);
            }
    
            private void removeDetachedView(View child, boolean animate) {
                child.setAccessibilityDelegate(null);
                AbsListView.this.removeDetachedView(child, animate);
            }
        }
    

    相关文章

      网友评论

          本文标题:2019-10-26

          本文链接:https://www.haomeiwen.com/subject/iwbyvctx.html