Recycleview之setAdapter源码分析

Recyclerview.java

public void setAdapter(@Nullable Adapter adapter) {
        // bail out if layout is frozen
        // 如果布局被冻结的话,重新激活布局
        setLayoutFrozen(false);
        //真正设置适配器的方法.
        setAdapterInternal(adapter, false, true);
        //设置数据集将要完全改变
        processDataSetCompletelyChanged(false);
        //请求重新布局
        requestLayout();
    }

 setAdapter主要是调用了setLayoutFrozen方法和setAdapterInternal方法.接下来就分析这两个方法.

Recyclerview.java

 public void setLayoutFrozen(boolean frozen) {
         //mLayoutFrozen默认就是false,用来控制是否冻结布局的.当设置为true时,就会冻结布局和滑动
        if (frozen != mLayoutFrozen) {
            //如果recycleview正在layout或者scroll就抛出异常
            assertNotInLayoutOrScroll("Do not setLayoutFrozen in layout or scroll");
            if (!frozen) {
                //frozen为false,表示要重新激活布局,所以将mLayoutFrozen设置为false
                mLayoutFrozen = false;
                if (mLayoutRequestEaten && mLayout != null && mAdapter != null) {
                    //请求重新布局
                    requestLayout();
                }
                mLayoutRequestEaten = false;
            } else {
                final long now = SystemClock.uptimeMillis();
                //获取ACTION_CANCEL事件
                MotionEvent cancelEvent = MotionEvent.obtain(now, now,
                        MotionEvent.ACTION_CANCEL, 0.0f, 0.0f, 0);
                 //执行ACTION_CANCEL的逻辑,取消滑动相关的配置
                onTouchEvent(cancelEvent);
                //同时将mLayoutFrozen 设置为true,冻结布局和滑动
                mLayoutFrozen = true;
                mIgnoreMotionEventTillDown = true;
                //停止一切滑动
                stopScroll();
            }
        }
    }

setLayoutFrozen方法主要是在设置真正的适配器之前确保布局是激活的.下面在看看setAdapterInternal方法.

Recyclerview.java

 private void setAdapterInternal(Adapter adapter, boolean compatibleWithPrevious,
            boolean removeAndRecycleViews) {
        if (mAdapter != null) {
        // mAdapter表示原来的适配器,如果不为null,就取消注册的观察者和recycleview的关联.           
            mAdapter.unregisterAdapterDataObserver(mObserver);
            mAdapter.onDetachedFromRecyclerView(this);
        }
        if (!compatibleWithPrevious || removeAndRecycleViews) {
            // 停止所有的动画
            if (mItemAnimator != null) {
                mItemAnimator.endAnimations();
            }
            //mLayout就是layoutmanager布局管理器,用来管理布局和滑动的,mRecycler就是Recycle用来管理item的复用的.这里就是清除掉之前的item.
            if (mLayout != null) {
                mLayout.removeAndRecycleAllViews(mRecycler);
                mLayout.removeAndRecycleScrapInt(mRecycler);
            }

            mRecycler.clear();
        }
        mAdapterHelper.reset();
        //用oldAdapter记录之前的适配器,将新的适配器赋值给mAdapter
        final Adapter oldAdapter = mAdapter;
        mAdapter = adapter;
        //给适配器注册观察者并且和recycleview关联起来
        if (adapter != null) {
            adapter.registerAdapterDataObserver(mObserver);
            adapter.onAttachedToRecyclerView(this);
        }
        //将2个适配器传给mLayout
        if (mLayout != null) {
            mLayout.onAdapterChanged(oldAdapter, mAdapter);
        }
        mRecycler.onAdapterChanged(oldAdapter, mAdapter, compatibleWithPrevious);
        mState.mStructureChanged = true;
        markKnownViewsInvalid();
    }

通过分析setAdapterInternal的源码,我们知道该方法的主要动作是,移除LayoutManager中的视图、清空Recycler mRecycler中的数据、更新Adapter mAdapter域、注册数据观察者、Adapter和RecyclerView绑定; 

 接下来,我们主要关心setAdapter之后是如何重新绑定数据,刷新View,即调用

onBindViewHolder(@NonNull VH holder, int position, @NonNull List<Object> payloads) 方法的过程

Recyclerview.java

@Override
    public void requestLayout() {
        //setAdapter的时候设置了mLayoutFrozen为false,所以会调用super.requestLayout();
        if (mInterceptRequestLayoutDepth == 0 && !mLayoutFrozen) {
            super.requestLayout();
        } else {
            mLayoutWasDefered = true;
        }
    }

最终会调用measure、layout、draw方法来进行View的三大工作流程

Recyclerview.java

protected void onMeasure(int widthSpec, int heightSpec) {
        if (mLayout == null) {
            defaultOnMeasure(widthSpec, heightSpec);
            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;
            }
            .....
    }

 onMeasure中会调用dispatchLayoutStep2()方法,进行实际布局。

Recyclerview.java

private void dispatchLayoutStep2() {
        startInterceptRequestLayout();
        onEnterLayoutOrScroll();
        mState.assertLayoutStep(State.STEP_LAYOUT | State.STEP_ANIMATIONS);
        mAdapterHelper.consumeUpdatesInOnePass();
        mState.mItemCount = mAdapter.getItemCount();
        mState.mDeletedInvisibleItemCountSincePreviousLayout = 0;

        // Step 2: Run layout
        mState.mInPreLayout = false;
        //布局管理器 布局给定适配器中的所有相关子视图。
        mLayout.onLayoutChildren(mRecycler, mState);

        mState.mStructureChanged = false;
        mPendingSavedState = null;

        // onLayoutChildren may have caused client code to disable item animations; re-check
        mState.mRunSimpleAnimations = mState.mRunSimpleAnimations && mItemAnimator != null;
        mState.mLayoutStep = State.STEP_ANIMATIONS;
        onExitLayoutOrScroll();
        stopInterceptRequestLayout(false);
    }

 dispatchLayoutStep2()方法中调用mLayout.onLayoutChildren(mRecycler, mState);即使用布局管理器 布局给定适配器中的所有相关子视图。这里以LinearLayoutManager为视角查看实现

LinearLayoutManager.java

public void onLayoutChildren(RecyclerView.Recycler recycler, RecyclerView.State state) {

    ........

    if (mAnchorInfo.mLayoutFromEnd) {
            // fill towards start
            updateLayoutStateToFillStart(mAnchorInfo);
            mLayoutState.mExtra = extraForStart;
            //填充由mLayoutState定义的给定布局
            fill(recycler, mLayoutState, state, false);
            startOffset = mLayoutState.mOffset;
            final int firstElement = mLayoutState.mCurrentPosition;
            if (mLayoutState.mAvailable > 0) {
                extraForEnd += mLayoutState.mAvailable;
            }
            // fill towards end
            updateLayoutStateToFillEnd(mAnchorInfo);
            mLayoutState.mExtra = extraForEnd;
            mLayoutState.mCurrentPosition += mLayoutState.mItemDirection;
            fill(recycler, mLayoutState, state, false);
            endOffset = mLayoutState.mOffset;

            if (mLayoutState.mAvailable > 0) {
                // end could not consume all. add more items towards start
                extraForStart = mLayoutState.mAvailable;
                updateLayoutStateToFillStart(firstElement, startOffset);
                mLayoutState.mExtra = extraForStart;
                fill(recycler, mLayoutState, state, false);
                startOffset = mLayoutState.mOffset;
            }
        } else {
            // fill towards end
            updateLayoutStateToFillEnd(mAnchorInfo);
            mLayoutState.mExtra = extraForEnd;
            //填充由mLayoutState定义的给定布局
            fill(recycler, mLayoutState, state, false);
            endOffset = mLayoutState.mOffset;
            final int lastElement = mLayoutState.mCurrentPosition;
            if (mLayoutState.mAvailable > 0) {
                extraForStart += mLayoutState.mAvailable;
            }
            // fill towards start
            updateLayoutStateToFillStart(mAnchorInfo);
            mLayoutState.mExtra = extraForStart;
            mLayoutState.mCurrentPosition += mLayoutState.mItemDirection;
            fill(recycler, mLayoutState, state, false);
            startOffset = mLayoutState.mOffset;

            if (mLayoutState.mAvailable > 0) {
                extraForEnd = mLayoutState.mAvailable;
                // start could not consume all it should. add more items towards end
                updateLayoutStateToFillEnd(lastElement, endOffset);
                mLayoutState.mExtra = extraForEnd;
                fill(recycler, mLayoutState, state, false);
                endOffset = mLayoutState.mOffset;
            }
        }
        .......
}

LinearLayoutManager的onLayoutChildren(RecyclerView.Recycler recycler, RecyclerView.State state)方法,里面调用了

fill(RecyclerView.Recycler recycler, LayoutState layoutState, RecyclerView.State state, boolean stopOnFocusable)

即填充由mLayoutState定义的给定布局

LinearLayoutManager.java

int fill(RecyclerView.Recycler recycler, LayoutState layoutState,
            RecyclerView.State state, boolean stopOnFocusable) {
        // max offset we should set is mFastScroll + available
        final int start = layoutState.mAvailable;
        if (layoutState.mScrollingOffset != LayoutState.SCROLLING_OFFSET_NaN) {
            // TODO ugly bug fix. should not happen
            if (layoutState.mAvailable < 0) {
                layoutState.mScrollingOffset += layoutState.mAvailable;
            }
            recycleByLayoutState(recycler, layoutState);
        }
        int remainingSpace = layoutState.mAvailable + layoutState.mExtra;
        LayoutChunkResult layoutChunkResult = mLayoutChunkResult;
        while ((layoutState.mInfinite || remainingSpace > 0) && layoutState.hasMore(state)) {
            layoutChunkResult.resetInternal();
            if (RecyclerView.VERBOSE_TRACING) {
                TraceCompat.beginSection("LLM LayoutChunk");
            }
            //布局块
            layoutChunk(recycler, state, layoutState, layoutChunkResult);
            if (RecyclerView.VERBOSE_TRACING) {
                TraceCompat.endSection();
            }
            if (layoutChunkResult.mFinished) {
                break;
            }
            ...
        }
        if (DEBUG) {
            validateChildOrder();
        }
        return start - layoutState.mAvailable;
    }

 走了layoutChunk(RecyclerView.Recycler recycler, RecyclerView.State state,LayoutState layoutState, LayoutChunkResult result) 方法

LinearLayoutManager.java

void layoutChunk(RecyclerView.Recycler recycler, RecyclerView.State state,
            LayoutState layoutState, LayoutChunkResult result) {
        //获取要布局的下一个元素的视图
        View view = layoutState.next(recycler);
        if (view == null) {
            if (DEBUG && layoutState.mScrapList == null) {
                throw new RuntimeException("received null view when unexpected");
            }
            // if we are laying out views in scrap, this may return null which means there is
            // no more items to layout.
            result.mFinished = true;
            return;
        }
        ....
}

 layoutChunk方法内部调用了LayoutState的next(RecyclerView.Recycler recycler)方法,获取要布局的下一个元素的视图

LayoutState.java

//获取要布局的下一个元素的视图。还根据{@link #mItemDirection}将当前项索引更新为下一个项。
View next(RecyclerView.Recycler recycler) {
            if (mScrapList != null) {
                return nextViewFromScrapList();
            }
            //获取为给定位置初始化的视图。
            final View view = recycler.getViewForPosition(mCurrentPosition);
            mCurrentPosition += mItemDirection;
            return view;
        }

 LayoutState的next方法内部调用了Recycler的getViewForPosition(int position)方法,用于获取为给定位置初始化的视图。

RecyclerView#Recycler.java

public View getViewForPosition(int position) {
            return getViewForPosition(position, false);
        }

 View getViewForPosition(int position, boolean dryRun) {
            //尝试从回收器废料、缓存、RecycledViewPool或直接创建它来获得给定位置的ViewHolder。
            return tryGetViewHolderForPositionByDeadline(position, dryRun, FOREVER_NS).itemView;
        }

最终调用了 tryGetViewHolderForPositionByDeadline(int position,boolean dryRun, long deadlineNs) 方法,尝试从回收器废料、缓存、RecycledViewPool或直接创建它来获得给定位置的ViewHolder。

RecyclerView#Recycler.java

ViewHolder tryGetViewHolderForPositionByDeadline(int position,
    boolean dryRun, long deadlineNs) {
        // 1) Find by position from scrap/hidden list/cache
        if (holder == null) {
            holder = getScrapOrHiddenOrCachedHolderForPosition(position, dryRun);
        }
        if (holder == null) {
            final int offsetPosition = mAdapterHelper.findPositionOffset(position);
            final int type = mAdapter.getItemViewType(offsetPosition);
            // 2) Find from scrap/cache via stable ids, if exists
            if (mAdapter.hasStableIds()) {
                holder = getScrapOrCachedViewForId(mAdapter.getItemId(offsetPosition),
                    type, dryRun);
                if (holder != null) {
                    // update position
                    holder.mPosition = offsetPosition;
                    fromScrapOrHiddenOrCache = true;
                }
            }
            if (holder == null && mViewCacheExtension != null) {
                // We are NOT sending the offsetPosition because LayoutManager does not
                // know it.
                final View view = mViewCacheExtension
                    .getViewForPositionAndType(this, position, type);
                if (view != null) {
                    holder = getChildViewHolder(view);
                    if (holder == null) {
                        throw new IllegalArgumentException("getViewForPositionAndType returned"
                                + " a view which does not have a ViewHolder");
                    } else if (holder.shouldIgnore()) {
                        throw new IllegalArgumentException("getViewForPositionAndType returned"
                                + " a view that is ignored. You must call stopIgnoring before"
                                + " returning this view.");
                    }
                }
            }
            if (holder == null) { // fallback to pool
                holder = getRecycledViewPool().getRecycledView(type);
            }
            if (holder == null) {
                //当holder==null的时候,创建ViewHolder
                holder = mAdapter.createViewHolder(RecyclerView.this, type);
            }
        }
        .....
        boolean bound = false;
        if (mState.isPreLayout() && holder.isBound()) {
            // do not update unless we absolutely have to.
            holder.mPreLayoutPosition = position;
        } else if (!holder.isBound() || holder.needsUpdate() || holder.isInvalid()) {
            if (DEBUG && holder.isRemoved()) {
                throw new IllegalStateException("Removed holder should be bound and it should"
                        + " come here only in pre-layout. Holder: " + holder);
            }
            final int offsetPosition = mAdapterHelper.findPositionOffset(position);
            //尝试绑定视图,并考虑相关的时间信息。如果deadlineNs != FOREVER_NS,此方法可能绑定失败,并返回false。
            bound = tryBindViewHolderByDeadline(holder, offsetPosition, position, deadlineNs);
        }

        final ViewGroup.LayoutParams lp = holder.itemView.getLayoutParams();
        final LayoutParams rvLayoutParams;
        if (lp == null) {
            rvLayoutParams = (LayoutParams) generateDefaultLayoutParams();
            holder.itemView.setLayoutParams(rvLayoutParams);
        } else if (!checkLayoutParams(lp)) {
            rvLayoutParams = (LayoutParams) generateLayoutParams(lp);
            holder.itemView.setLayoutParams(rvLayoutParams);
        } else {
            rvLayoutParams = (LayoutParams) lp;
        }
        rvLayoutParams.mViewHolder = holder;
        rvLayoutParams.mPendingInvalidate = fromScrapOrHiddenOrCache && bound;
        return holder;
    }

 内部调用了tryBindViewHolderByDeadline(ViewHolder holder, int offsetPosition,int position, long deadlineNs)方法,

尝试绑定视图,并考虑相关的时间信息。如果deadlineNs != FOREVER_NS,此方法可能绑定失败,并返回false。

RecyclerView#Recycler.java

private boolean tryBindViewHolderByDeadline(ViewHolder holder, int offsetPosition,
                int position, long deadlineNs) {
            holder.mOwnerRecyclerView = RecyclerView.this;
            final int viewType = holder.getItemViewType();
            long startBindNs = getNanoTime();
            if (deadlineNs != FOREVER_NS
                    && !mRecyclerPool.willBindInTime(viewType, startBindNs, deadlineNs)) {
                // abort - we have a deadline we can't meet
                return false;
            }
            //绑定ViewHolder
            mAdapter.bindViewHolder(holder, offsetPosition);
            long endBindNs = getNanoTime();
            mRecyclerPool.factorInBindTime(holder.getItemViewType(), endBindNs - startBindNs);
            attachAccessibilityDelegate(holder.itemView);
            if (mState.isPreLayout()) {
                holder.mPreLayoutPosition = position;
            }
            return true;
        }

RecyclerView#Adapter.java

public final void bindViewHolder(VH holder, int position) {
            holder.mPosition = position;
            if (hasStableIds()) {
                holder.mItemId = getItemId(position);
            }
            holder.setFlags(ViewHolder.FLAG_BOUND,
                    ViewHolder.FLAG_BOUND | ViewHolder.FLAG_UPDATE | ViewHolder.FLAG_INVALID
                            | ViewHolder.FLAG_ADAPTER_POSITION_UNKNOWN);
            Trace.beginSection(TRACE_BIND_VIEW_TAG);
            //子类重写该方法,以显示指定位置的数据
            onBindViewHolder(holder, position, holder.getUnmodifiedPayloads());
            holder.clearPayload();
            final ViewGroup.LayoutParams layoutParams = holder.itemView.getLayoutParams();
            if (layoutParams instanceof RecyclerView.LayoutParams) {
                ((LayoutParams) layoutParams).mInsetsDirty = true;
            }
            Trace.endSection();
        }

 public void onBindViewHolder(VH holder, int position, List<Object> payloads) {
            onBindViewHolder(holder, position);
        }

public abstract void onBindViewHolder(VH holder, int position);

最终调用了onBindViewHolder(holder, position, holder.getUnmodifiedPayloads());方法

子类重写该方法,以显示指定位置的数据。   

  • 0
    点赞
  • 3
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值