RecycleView源码解析 - 1835434698/1835434698.github.io GitHub Wiki

mRunPredictiveAnimations

RecyclerView源码解析

1、构造方法

三个构造方法最终都是调用的三参的构造方法。

public RecyclerView(@NonNull Context context) {
    this(context, null);
}

public RecyclerView(@NonNull Context context, @Nullable AttributeSet attrs) {
    this(context, attrs, 0);
}

public RecyclerView(@NonNull Context context, @Nullable AttributeSet attrs, int defStyle) {
        super(context, attrs, defStyle);
        if (attrs != null) {
            TypedArray a = context.obtainStyledAttributes(attrs, CLIP_TO_PADDING_ATTR, defStyle, 0);//加载资源文件中的样式
            mClipToPadding = a.getBoolean(0, true);
            a.recycle();
        } else {
            mClipToPadding = true;
        }
        setScrollContainer(true);//设置可滚动
        setFocusableInTouchMode(true);//设置可以获取焦点

        final ViewConfiguration vc = ViewConfiguration.get(context);//获取vie的配置
        mTouchSlop = vc.getScaledTouchSlop();
        mScaledHorizontalScrollFactor =
                ViewConfigurationCompat.getScaledHorizontalScrollFactor(vc, context);
        mScaledVerticalScrollFactor =
                ViewConfigurationCompat.getScaledVerticalScrollFactor(vc, context);
        mMinFlingVelocity = vc.getScaledMinimumFlingVelocity();
        mMaxFlingVelocity = vc.getScaledMaximumFlingVelocity();
        setWillNotDraw(getOverScrollMode() == View.OVER_SCROLL_NEVER);

        mItemAnimator.setListener(mItemAnimatorListener);//设置动画效果
        initAdapterManager();//初始化adapterManager 调用01
        initChildrenHelper();//初始化子view的高  调用02
        initAutofill();//初始化自动填充 调用03
        // If not explicitly specified this view is important for accessibility.
        if (ViewCompat.getImportantForAccessibility(this)
                == ViewCompat.IMPORTANT_FOR_ACCESSIBILITY_AUTO) {
            ViewCompat.setImportantForAccessibility(this,
                    ViewCompat.IMPORTANT_FOR_ACCESSIBILITY_YES);
        }
        mAccessibilityManager = (AccessibilityManager) getContext()
                .getSystemService(Context.ACCESSIBILITY_SERVICE);
        setAccessibilityDelegateCompat(new RecyclerViewAccessibilityDelegate(this));
        // Create the layoutManager if specified.

        boolean nestedScrollingEnabled = true;

        if (attrs != null) {
            int defStyleRes = 0;
            TypedArray a = context.obtainStyledAttributes(attrs, R.styleable.RecyclerView,
                    defStyle, defStyleRes);
            String layoutManagerName = a.getString(R.styleable.RecyclerView_layoutManager);
            int descendantFocusability = a.getInt(
                    R.styleable.RecyclerView_android_descendantFocusability, -1);
            if (descendantFocusability == -1) {
                setDescendantFocusability(ViewGroup.FOCUS_AFTER_DESCENDANTS);
            }
            mEnableFastScroller = a.getBoolean(R.styleable.RecyclerView_fastScrollEnabled, false);
            if (mEnableFastScroller) {
                StateListDrawable verticalThumbDrawable = (StateListDrawable) a
                        .getDrawable(R.styleable.RecyclerView_fastScrollVerticalThumbDrawable);
                Drawable verticalTrackDrawable = a
                        .getDrawable(R.styleable.RecyclerView_fastScrollVerticalTrackDrawable);
                StateListDrawable horizontalThumbDrawable = (StateListDrawable) a
                        .getDrawable(R.styleable.RecyclerView_fastScrollHorizontalThumbDrawable);
                Drawable horizontalTrackDrawable = a
                        .getDrawable(R.styleable.RecyclerView_fastScrollHorizontalTrackDrawable);
                initFastScroller(verticalThumbDrawable, verticalTrackDrawable,
                        horizontalThumbDrawable, horizontalTrackDrawable);
            }
            a.recycle();
            createLayoutManager(context, layoutManagerName, attrs, defStyle, defStyleRes);

            if (Build.VERSION.SDK_INT >= 21) {
                a = context.obtainStyledAttributes(attrs, NESTED_SCROLLING_ATTRS,
                        defStyle, defStyleRes);
                nestedScrollingEnabled = a.getBoolean(0, true);
                a.recycle();
            }
        } else {
            setDescendantFocusability(ViewGroup.FOCUS_AFTER_DESCENDANTS);
        }

        // Re-set whether nested scrolling is enabled so that it is set on all API levels
        setNestedScrollingEnabled(nestedScrollingEnabled);
    }

//01 
void initAdapterManager() {
    //adapterHelper 维护adapter的状态等。后面再细讲
        mAdapterHelper = new AdapterHelper(new AdapterHelper.Callback() {
            @Override
            public ViewHolder findViewHolder(int position) {
                //根据position获取到viewHolder
                final ViewHolder vh = findViewHolderForPosition(position, true);
                if (vh == null) {
                    return null;
                }
                // ensure it is not hidden because for adapter helper, the only thing matter is that
                // LM thinks view is a child.
                if (mChildHelper.isHidden(vh.itemView)) {//判断该View是否显示在屏幕 调用 01-01
                    if (DEBUG) {
                        Log.d(TAG, "assuming view holder cannot be find because it is hidden");
                    }
                    return null;
                }
                return vh;
            }
@Override
        public void offsetPositionsForRemovingInvisible(int start, int count) {
            offsetPositionRecordsForRemove(start, count, true);
            mItemsAddedOrRemoved = true;
            mState.mDeletedInvisibleItemCountSincePreviousLayout += count;
        }

        @Override
        public void offsetPositionsForRemovingLaidOutOrNewView(
                int positionStart, int itemCount) {
            offsetPositionRecordsForRemove(positionStart, itemCount, false);
            mItemsAddedOrRemoved = true;
        }       
@Override
        public void markViewHoldersUpdated(int positionStart, int itemCount, Object payload) {
            viewRangeUpdate(positionStart, itemCount, payload);
            mItemsChanged = true;
        }

        @Override
        public void onDispatchFirstPass(AdapterHelper.UpdateOp op) {
            dispatchUpdate(op);
        }

        void dispatchUpdate(AdapterHelper.UpdateOp op) {
            switch (op.cmd) {
                case AdapterHelper.UpdateOp.ADD:
                    mLayout.onItemsAdded(RecyclerView.this, op.positionStart, op.itemCount);
                    break;
                case AdapterHelper.UpdateOp.REMOVE:
                    mLayout.onItemsRemoved(RecyclerView.this, op.positionStart, op.itemCount);
                    break;
                case AdapterHelper.UpdateOp.UPDATE:
                    mLayout.onItemsUpdated(RecyclerView.this, op.positionStart, op.itemCount,
                            op.payload);
                    break;
                case AdapterHelper.UpdateOp.MOVE:
                    mLayout.onItemsMoved(RecyclerView.this, op.positionStart, op.itemCount, 1);
                    break;
            }
        }

        @Override
        public void onDispatchSecondPass(AdapterHelper.UpdateOp op) {
            dispatchUpdate(op);
        }

        @Override
        public void offsetPositionsForAdd(int positionStart, int itemCount) {
            offsetPositionRecordsForInsert(positionStart, itemCount);
            mItemsAddedOrRemoved = true;
        }

        @Override
        public void offsetPositionsForMove(int from, int to) {
            offsetPositionRecordsForMove(from, to);
            // should we create mItemsMoved ?
            mItemsAddedOrRemoved = true;
        }
    });
}

class ChildHelper{
	final List<View> mHiddenViews;//缓存的屏幕之外的view视图
    
    //01-01 ChildHelper的方法
    boolean isHidden(View view) {
        return mHiddenViews.contains(view);
    }    
}
//02
private void initChildrenHelper() {
    //子View帮助类
    mChildHelper = new ChildHelper(new ChildHelper.Callback() {
        @Override
        public int getChildCount() {
            return RecyclerView.this.getChildCount();//获取子View的数量
        }

        @Override
        public void addView(View child, int index) {
            if (VERBOSE_TRACING) {
                TraceCompat.beginSection("RV addView");
            }
            RecyclerView.this.addView(child, index);//添加子view
            if (VERBOSE_TRACING) {
                TraceCompat.endSection();
            }
            dispatchChildAttached(child);
        }

        @Override
        public int indexOfChild(View view) {
            return RecyclerView.this.indexOfChild(view);//得到子view的位置
        }

        @Override
        public void removeViewAt(int index) {//移除指定位置View
            final View child = RecyclerView.this.getChildAt(index);
            if (child != null) {
                dispatchChildDetached(child);

                // Clear any android.view.animation.Animation that may prevent the item from
                // detaching when being removed. If a child is re-added before the
                // lazy detach occurs, it will receive invalid attach/detach sequencing.
                child.clearAnimation();
            }
            if (VERBOSE_TRACING) {
                TraceCompat.beginSection("RV removeViewAt");
            }
            RecyclerView.this.removeViewAt(index);
            if (VERBOSE_TRACING) {
                TraceCompat.endSection();
            }
        }

        @Override
        public View getChildAt(int offset) {
            return RecyclerView.this.getChildAt(offset);//得到指定位置的子view
        }

        @Override
        public void removeAllViews() {//移除所有View
            final int count = getChildCount();
            for (int i = 0; i < count; i++) {
                View child = getChildAt(i);
                dispatchChildDetached(child);

                // Clear any android.view.animation.Animation that may prevent the item from
                // detaching when being removed. If a child is re-added before the
                // lazy detach occurs, it will receive invalid attach/detach sequencing.
                child.clearAnimation();
            }
            RecyclerView.this.removeAllViews();
        }

        @Override
        public ViewHolder getChildViewHolder(View view) {
            return getChildViewHolderInt(view);//根据View的到ViewHolder
        }

        @Override
        public void attachViewToParent(View child, int index,
                ViewGroup.LayoutParams layoutParams) {
            final ViewHolder vh = getChildViewHolderInt(child);
            if (vh != null) {
                if (!vh.isTmpDetached() && !vh.shouldIgnore()) {
                    throw new IllegalArgumentException("Called attach on a child which is not"
                            + " detached: " + vh + exceptionLabel());
                }
                if (DEBUG) {
                    Log.d(TAG, "reAttach " + vh);
                }
                vh.clearTmpDetachFlag();
            }
            RecyclerView.this.attachViewToParent(child, index, layoutParams);//添加到父View
        }

        @Override
        public void detachViewFromParent(int offset) {
            final View view = getChildAt(offset);
            if (view != null) {
                final ViewHolder vh = getChildViewHolderInt(view);
                if (vh != null) {
                    if (vh.isTmpDetached() && !vh.shouldIgnore()) {
                        throw new IllegalArgumentException("called detach on an already"
                                + " detached child " + vh + exceptionLabel());
                    }
                    if (DEBUG) {
                        Log.d(TAG, "tmpDetach " + vh);
                    }
                    vh.addFlags(ViewHolder.FLAG_TMP_DETACHED);
                }
            }
            RecyclerView.this.detachViewFromParent(offset);//分离从父View
        }

        @Override
        public void onEnteredHiddenState(View child) {
            final ViewHolder vh = getChildViewHolderInt(child);
            if (vh != null) {
                vh.onEnteredHiddenState(RecyclerView.this);//隐藏子View
            }
        }

        @Override
        public void onLeftHiddenState(View child) {
            final ViewHolder vh = getChildViewHolderInt(child);
            if (vh != null) {
                vh.onLeftHiddenState(RecyclerView.this);
            }
        }
    });
}
//03
private void initAutofill() {
    if (ViewCompat.getImportantForAutofill(this) == View.IMPORTANT_FOR_AUTOFILL_AUTO) {
        ViewCompat.setImportantForAutofill(this,
                View.IMPORTANT_FOR_AUTOFILL_NO_EXCLUDE_DESCENDANTS);
    }
}
//04
protected void onMeasure(int widthSpec, int heightSpec) {
    if (mLayout == null) {
        defaultOnMeasure(widthSpec, heightSpec);//测量宽高, 调用 04-01
        return;
    }
    if (mLayout.isAutoMeasureEnabled()) {
        final int widthMode = MeasureSpec.getMode(widthSpec);
        final int heightMode = MeasureSpec.getMode(heightSpec);

        /**
         * This specific call should be considered deprecated and replaced with
         * {@link #defaultOnMeasure(int, int)}. It can't actually be replaced as it could
         * break existing third party code but all documentation directs developers to not
         * override {@link LayoutManager#onMeasure(int, int)} when
         * {@link LayoutManager#isAutoMeasureEnabled()} returns true.
         */
        mLayout.onMeasure(mRecycler, mState, widthSpec, heightSpec);

        final boolean measureSpecModeIsExactly =
                widthMode == MeasureSpec.EXACTLY && heightMode == MeasureSpec.EXACTLY;
        if (measureSpecModeIsExactly || mAdapter == null) {
            return;
        }

        if (mState.mLayoutStep == State.STEP_START) {
            dispatchLayoutStep1();
        }
        // set dimensions in 2nd step. Pre-layout should happen with old dimensions for
        // consistency
        mLayout.setMeasureSpecs(widthSpec, heightSpec);
        mState.mIsMeasuring = true;
        dispatchLayoutStep2();

        // now we can get the width and height from the children.
        mLayout.setMeasuredDimensionFromChildren(widthSpec, heightSpec);

        // if RecyclerView has non-exact width and height and if there is at least one child
        // which also has non-exact width & height, we have to re-measure.
        if (mLayout.shouldMeasureTwice()) {
            mLayout.setMeasureSpecs(
                    MeasureSpec.makeMeasureSpec(getMeasuredWidth(), MeasureSpec.EXACTLY),
                    MeasureSpec.makeMeasureSpec(getMeasuredHeight(), MeasureSpec.EXACTLY));
            mState.mIsMeasuring = true;
            dispatchLayoutStep2();
            // now we can get the width and height from the children.
            mLayout.setMeasuredDimensionFromChildren(widthSpec, heightSpec);
        }
    } else {
        if (mHasFixedSize) {
            mLayout.onMeasure(mRecycler, mState, widthSpec, heightSpec);
            return;
        }
        // custom onMeasure
        if (mAdapterUpdateDuringMeasure) {
            startInterceptRequestLayout();
            onEnterLayoutOrScroll();
            processAdapterUpdatesAndSetAnimationFlags();
            onExitLayoutOrScroll();

            if (mState.mRunPredictiveAnimations) {
                mState.mInPreLayout = true;
            } else {
                // consume remaining updates to provide a consistent state with the layout pass.
                mAdapterHelper.consumeUpdatesInOnePass();
                mState.mInPreLayout = false;
            }
            mAdapterUpdateDuringMeasure = false;
            stopInterceptRequestLayout(false);
        } else if (mState.mRunPredictiveAnimations) {
            // If mAdapterUpdateDuringMeasure is false and mRunPredictiveAnimations is true:
            // this means there is already an onMeasure() call performed to handle the pending
            // adapter change, two onMeasure() calls can happen if RV is a child of LinearLayout
            // with layout_width=MATCH_PARENT. RV cannot call LM.onMeasure() second time
            // because getViewForPosition() will crash when LM uses a child to measure.
            setMeasuredDimension(getMeasuredWidth(), getMeasuredHeight());
            return;
        }

        if (mAdapter != null) {
            mState.mItemCount = mAdapter.getItemCount();//调用RecyclerView.Adapter的getItemCount
        } else {
            mState.mItemCount = 0;
        }
        startInterceptRequestLayout();
        mLayout.onMeasure(mRecycler, mState, widthSpec, heightSpec);
        stopInterceptRequestLayout(false);
        mState.mInPreLayout = false; // clear
    }
}

//04-01
void defaultOnMeasure(int widthSpec, int heightSpec) {
        // calling LayoutManager here is not pretty but that API is already public and it is better
        // than creating another method since this is internal.
        final int width = LayoutManager.chooseSize(widthSpec,
                getPaddingLeft() + getPaddingRight(),
                ViewCompat.getMinimumWidth(this));
        final int height = LayoutManager.chooseSize(heightSpec,
                getPaddingTop() + getPaddingBottom(),
                ViewCompat.getMinimumHeight(this));

        setMeasuredDimension(width, height);
    }
//05
protected void onLayout(boolean changed, int l, int t, int r, int b) {
    TraceCompat.beginSection(TRACE_ON_LAYOUT_TAG);
    dispatchLayout();//分发布局, 调用 05-01
    TraceCompat.endSection();
    mFirstLayoutComplete = true;
}

void dispatchLayout() {
        if (mAdapter == null) {
            Log.e(TAG, "No adapter attached; skipping layout");
            // leave the state in START
            return;
        }
        if (mLayout == null) {
            Log.e(TAG, "No layout manager attached; skipping layout");
            // leave the state in START
            return;
        }
        mState.mIsMeasuring = false;
        if (mState.mLayoutStep == State.STEP_START) {
            dispatchLayoutStep1();//内部通过调用LayoutManager然后又调用到 onCreateViewHolder与onBindViewHolder
            mLayout.setExactMeasureSpecsFrom(this);
            dispatchLayoutStep2();
        } else if (mAdapterHelper.hasUpdates() || mLayout.getWidth() != getWidth()
                || mLayout.getHeight() != getHeight()) {
            // First 2 steps are done in onMeasure but looks like we have to run again due to
            // changed size.
            mLayout.setExactMeasureSpecsFrom(this);
            dispatchLayoutStep2();
        } else {
            // always make sure we sync them (to ensure mode is exact)
            mLayout.setExactMeasureSpecsFrom(this);
        }
        dispatchLayoutStep3();
    }
//06
public void onDraw(Canvas c) {
    super.onDraw(c);

    final int count = mItemDecorations.size();
    for (int i = 0; i < count; i++) {
        mItemDecorations.get(i).onDraw(c, this, mState);//绘制动画
    }
}
//07
public void draw(Canvas c) {
    super.draw(c);

    final int count = mItemDecorations.size();
    for (int i = 0; i < count; i++) {
        mItemDecorations.get(i).onDrawOver(c, this, mState);//动画绘制结束。
    }
    // TODO If padding is not 0 and clipChildrenToPadding is false, to draw glows properly, we
    // need find children closest to edges. Not sure if it is worth the effort.
    boolean needsInvalidate = false;
    if (mLeftGlow != null && !mLeftGlow.isFinished()) {
        final int restore = c.save();
        final int padding = mClipToPadding ? getPaddingBottom() : 0;
        c.rotate(270);
        c.translate(-getHeight() + padding, 0);
        needsInvalidate = mLeftGlow != null && mLeftGlow.draw(c);
        c.restoreToCount(restore);
    }
    if (mTopGlow != null && !mTopGlow.isFinished()) {
        final int restore = c.save();
        if (mClipToPadding) {
            c.translate(getPaddingLeft(), getPaddingTop());
        }
        needsInvalidate |= mTopGlow != null && mTopGlow.draw(c);
        c.restoreToCount(restore);
    }
    if (mRightGlow != null && !mRightGlow.isFinished()) {
        final int restore = c.save();
        final int width = getWidth();
        final int padding = mClipToPadding ? getPaddingTop() : 0;
        c.rotate(90);
        c.translate(-padding, -width);
        needsInvalidate |= mRightGlow != null && mRightGlow.draw(c);
        c.restoreToCount(restore);
    }
    if (mBottomGlow != null && !mBottomGlow.isFinished()) {
        final int restore = c.save();
        c.rotate(180);
        if (mClipToPadding) {
            c.translate(-getWidth() + getPaddingRight(), -getHeight() + getPaddingBottom());
        } else {
            c.translate(-getWidth(), -getHeight());
        }
        needsInvalidate |= mBottomGlow != null && mBottomGlow.draw(c);
        c.restoreToCount(restore);
    }

    // If some views are animating, ItemDecorators are likely to move/change with them.
    // Invalidate RecyclerView to re-draw decorators. This is still efficient because children's
    // display lists are not invalidated.
    if (!needsInvalidate && mItemAnimator != null && mItemDecorations.size() > 0
            && mItemAnimator.isRunning()) {
        needsInvalidate = true;
    }

    if (needsInvalidate) {
        ViewCompat.postInvalidateOnAnimation(this);
    }
}
public final class Recycler {
    //4级缓存
    final ArrayList<ViewHolder> mAttachedScrap = new ArrayList<>();//存放全部的对象
    ArrayList<ViewHolder> mChangedScrap = null;//发生改变的对象  1级
    final ArrayList<ViewHolder> mCachedViews = new ArrayList<ViewHolder>();//存放remove调的视图的VIewHolder对象(保存的信息、绑定的数据未删除),容量有限制默认是2。  2级
    RecycledViewPool mRecyclerPool;//存放remove调的视图的VIewHolder对象(完全删除信息、数据等)。  3级
    
    private final List<ViewHolder>
            mUnmodifiableAttachedScrap = Collections.unmodifiableList(mAttachedScrap);//当前在报废列表中的不可修改的列表

    
    private int mRequestedCacheMax = DEFAULT_CACHE_SIZE;//最大请求缓存个数,默认2.
    int mViewCacheMax = DEFAULT_CACHE_SIZE;//最大View缓存个数,默认2.


    private ViewCacheExtension mViewCacheExtension; //缓存扩展类,支持用户自定义   4级

    static final int DEFAULT_CACHE_SIZE = 2;
}
⚠️ **GitHub.com Fallback** ⚠️