* This article has been published exclusively by guolin_blog, an official wechat account

From today, the building officially began to analyze RecyclerView source code. In order to read RecyclerView source code, the main building specifically to see the View of the three processes, that is, the so-called brush equipment. Of course, when reading RecyclerView source code, also referred to other big guy’s article, this article as far as possible to post a better article, is the so-called stone of another mountain, can attack jade.

As the first article of the series, talk about why you need to read RecyclerView source code, mainly from three aspects. First, RecyclerView is very important in the actual development, now almost every APP will show a lot of data, list display is naturally a very good way, and RecyclerView plays a pivotal role in the list, so RecyclerView is often seen in the actual development, we get the fish, Must have a fish; Second, the online analysis of RecyclerView is full of articles, but most of the articles are fragmentary, there is no systematic analysis of RecyclerView, this paper intends to system analysis of RecyclerView, can also be said to be a collection of hundreds of long; Third, the owner of the building uses RecycleView very frequently, but does not have a deep understanding of its principle, so this is also an improvement of their own.

Reading the source code itself is a very boring and time-consuming thing, for the owner of their own, but also Alexander, afraid of their own lack of experience, misleading students to learn, so if there are mistakes in the article, please you big guy guidance.

This series of articles is going to start in a few places. At first, RecyclerView was regarded as a common View, and its three processes and event transfer (including nested sliding) were analyzed respectively. Then the analysis of RecyclerView cache principle, which is the essence of RecyclerView; Then analyze the Adapter of RecyclerView, LayoutManager, ItemAnimator and ItemDecoration. Finally, it is the extension of RecyclerView, including the custom of LayoutManager and the use of RecyclerView common pits.

See the list written above, I can not help but leave a cold sweat, the original RecyclerView has so much content, really worried that they can not complete the task 馃槀.

1. An overview of the

Before the analysis of RecyclerView source code, we still have a preliminary understanding of RecyclerView, a simple understanding of what it is, its basic structure.

RecyclerView is Google dad put forward in the IO conference in 2014 (it seems that the age of RecyclerView is still relatively old 馃槀), the specific purpose is not used to replace ListView, the creator also do not know, because at that time the creator is still reading the second year of high school. But in the actual development, since there is RecyclerView, ListView and GridView are rarely used, so we temporarily think that the purpose of RecyclerView is to replace ListView and GridView.

RecyclerView itself is a display of a large number of data controls, compared to ListView,RecyclerView level 4 cache (some people say it is level 3 cache, these are not important 馃槀) on the performance is very good, in terms of performance compared to ListView improved a lot. At the same time, because of the existence of LayoutManager, RecyclerView not only has the characteristics of ListView, but also the characteristics of GridView. That’s probably one of the reasons RecyclerView is so popular.

RecyclerView is also very flexible in terms of design, with different parts assuming different responsibilities. The Adapter is responsible for providing data, including creating ViewHolder and binding data, LayoutManager is responsible for measuring and laying out the ItemView, and ItemAnimator is responsible for animating each ItemView. ItemDecoration is responsible for the spacing of each ItemView. This pluggable architecture makes RecyclerView very flexible, and everyone can define different parts according to their own needs.

Because of this plug type design, RecyclerView in the use of a little bit more difficult than other controls, but this is not a matter, who called RecyclerView so popular 馃槀.


Well, like a little nonsense, now we formally to analyze the source code, the focus of this article is the three RecyclerView process.

Reference article:

  1. RecyclerView source code analysis (a) – drawing process
  2. RecyclerView analysis
  3. RecyclerView analysis — a continuation

Note that the source code of RecyclerView in this paper is from 27.1.1

2. measure

No matter how mysterious RecyclerView is, it is also a View, so the analysis of its three processes is very necessary. At the same time, if you know about RecyclerView students should know that the three processes of RecyclerView are very different from ordinary View.

First, let’s look at the measure process, let’s look at the onMeasure method of RecyclerView.

    protected void onMeasure(int widthSpec, int heightSpec) {
        if(mLayout == null) {if(mLayout isAutoMeasureEnabled ()) {/ / the second}else{// Third case}}Copy the code

The onMeasure method is still a bit long, so I’ve divided it up into three cases, and I’ll explain them briefly.

  1. mLayoutnamelyLayoutManagerThe object. We know whenRecyclerViewtheLayoutManagerIs empty,RecyclerViewCan’t display any data, here we find the answer.
  2. LayoutManagerThis is the case when you have automatic measurement turned on. In this case, it is possible to measure twice.
  3. The third case is the case where automatic measurement is not turned on, which is relatively rare, because in order toRecyclerViewsupportwarp_contentProperty, provided by the systemLayoutManagerIt’s all on automatic, but we still have to analyze it.

So let’s do the first case.

(1). When LayoutManager is empty

In this case, it’s easier. Let’s look at the source code:

        if (mLayout == null) {
            defaultOnMeasure(widthSpec, heightSpec);
            return;
        }
Copy the code

We call defaultOnMeasure directly, so let’s move on to defaultOnMeasure.

    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);
    }
Copy the code

In the defaultOnMeasure method, the values are calculated using LayoutManager’s chooseSize method, followed by the setMeasuredDimension method. Let’s take a look:

        public static int chooseSize(int spec, int desired, int min) {
            final int mode = View.MeasureSpec.getMode(spec);
            final int size = View.MeasureSpec.getSize(spec);
            switch (mode) {
                case View.MeasureSpec.EXACTLY:
                    return size;
                case View.MeasureSpec.AT_MOST:
                    return Math.min(size, Math.max(desired, min));
                case View.MeasureSpec.UNSPECIFIED:
                default:
                    returnMath.max(desired, min); }}Copy the code

ChooseSize method to express the meaning is relatively simple, is through the RecyclerView measurement mode to obtain different values, here will not explain in detail.

So that’s the end of the first case. Because when LayoutManager is empty, the dispatchLayout method is called when RecyclerView is in the onLayout phase. The dispatchLayout method has this line of code:

        if (mLayout == null) {
            Log.e(TAG, "No layout manager attached; skipping layout");
            // leave the state in START
            return;
        }
Copy the code

Therefore, when LayoutManager is empty, it is natural that no data is displayed.

Now let’s look at the second case, which is the normal case.

(2). When LayoutManager turns on automatic measurement

Before we analyze this situation, let’s know a few things.

The measurement of RecyclerView is divided into two steps, calling dispatchLayoutStep1 and dispatchLayoutStep2 respectively. At the same time, students who understand RecyclerView source code should know that there is also a dispatchLayoutStep3 method in RecyclerView source code. These three methods have similar method names, so it can be confusing. This article will explain the functions of these three methods in detail.

Since only dispatchLayoutStep1 and dispatchLayoutStep2 are called in this case, we will focus on these two methods here. And dispatchLayoutStep3 method call in RecyclerView onLayout method inside, so in the later analysis of onLayout method to see dispatchLayoutStep3 method.

Before we do that, let’s look at something called mstate.mlayoutstep. There are several possible values for this variable. Let’s take a look at each:

The values meaning
State.STEP_START mState.mLayoutStepIn this case, RecyclerView is not yet experienceddispatchLayoutStep1Because thedispatchLayoutStep1After the callmState.mLayoutStepWill becomeState.STEP_LAYOUT.
State.STEP_LAYOUT whenmState.mLayoutStepforState.STEP_LAYOUTIs in the Layout phase, which is calleddispatchLayoutStep2methodslayout RecyclerViewthechildren. calldispatchLayoutStep2Method, at this pointmState.mLayoutStepInto theState.STEP_ANIMATIONS.
State.STEP_ANIMATIONS whenmState.mLayoutStepforState.STEP_ANIMATIONSSaid,RecyclerViewWe are in the third stage, where we execute the animation, which is calleddispatchLayoutStep3Methods. whendispatchLayoutStep3After the method is executed,mState.mLayoutStepAnd into theState.STEP_START.

From the table above, we know that the three states of mstate.mlayoutstep correspond to different dispatchLayoutStep methods. We need to be clear about this, or the rest of the code will be hard to understand.

All right, the foreplay is about ready, and now it’s time to go to the climax 馃槀. We started the formal analysis of the source code.

        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); }}Copy the code

I’ve divided this code into three steps. Let’s take a look:

  1. callLayoutManagertheonMeasureMethods To measure. foronMeasureMethods I also felt very confused and found traditionalLayoutManagerNone of them implement this method. We’ll look at this method briefly later.
  2. ifmState.mLayoutStepforState.STEP_STARTThen it will be executeddispatchLayoutStep1Method is then executeddispatchLayoutStep2Methods.
  3. If a second measurement is required, it is called againdispatchLayoutStep2Methods.

Above three steps, let’s analyze step by step. First, let’s look at the first step, which is also the onMeasure method.

What does LayoutManager’s onMeasure method do for us?

        public void onMeasure(Recycler recycler, State state, int widthSpec, int heightSpec) {
            mRecyclerView.defaultOnMeasure(widthSpec, heightSpec);
        }
Copy the code

The default is to call the RecyclerView defaultOnMeasure method, as for what defaultOnMeasure method does, this has been introduced in front, here will not be introduced.

The onMeasure method of View has two functions. One is to measure its own width and height. From the perspective of RecyclerView, it entrusted its measurement work to the onMeasure method of LayoutManager. Therefore, when we customize LayoutManager, we need to pay attention to the existence of onMeasure method. However, several official LayoutManagers have not overwritten this method. Therefore, it is best not to rewrite the onMeasure method of LayoutManager until all is well; The second is to measure the subview, but we haven’t seen the implementation yet.

Next, let’s look at the second step and see what the dispatchLayoutStep1 and dispatchLayoutStep2 methods actually do.

Before going into step 2, let’s have a general idea of these three methods.

The method name role
dispatchLayoutStep1 The big threedispatchLayoutStepMethod 1. There are three main functions of this method: 1AdapterUpdate; 2. Decide whether to executeItemAnimator; 3. SaveItemViewAnimation information. This method is also known as preLayout, whenAdapterUpdated, this method will save eachItemViewOld information (oldViewHolderInfo)
dispatchLayoutStep2 The big threedispatchLayoutStepMethod 2. In this method, it’s really going onchildrenMeasurement and layout.
dispatchLayoutStep3 The big threedispatchLayoutStepMethod 3. The effect of this method is implemented indispatchLayoutStep1Method to save the animation information. This method is not the focus of this article and will be introduced laterItemAnimatorWill focus on the analysis of this method.

Let’s go back to the onMeasure method and take a look at the whole process.

            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();
Copy the code

If mstate.mlayoutstep == state.step_start, the dispatchLayoutStep1 method is called, which corresponds to mLayoutStep. Now let’s look at the dispatchLayoutStep1 method

    private void dispatchLayoutStep1() {
        mState.assertLayoutStep(State.STEP_START);
        fillRemainingScrollValues(mState);
        mState.mIsMeasuring = false;
        startInterceptRequestLayout();
        mViewInfoStore.clear();
        onEnterLayoutOrScroll();
        processAdapterUpdatesAndSetAnimationFlags();
        saveFocusInfo();
        mState.mTrackOldChangeHolders = mState.mRunSimpleAnimations && mItemsChanged;
        mItemsAddedOrRemoved = mItemsChanged = false;
        mState.mInPreLayout = mState.mRunPredictiveAnimations;
        mState.mItemCount = mAdapter.getItemCount();
        findMinMaxChildLayoutPositions(mMinMaxLayoutPositions);

        if(mState. MRunSimpleAnimations) {/ / find ItemView is not remove, save OldViewHolder information, prepare preliminary layout}if(mState. MRunPredictiveAnimations) layout} {/ /.else {
            clearOldPositions();
        }
        onExitLayoutOrScroll();
        stopInterceptRequestLayout(false);
        mState.mLayoutStep = State.STEP_LAYOUT;
    }
Copy the code

This article will only briefly analyze this method, because it is closely related to ItemAnimator, which will be analyzed in detail in the subsequent introduction to ItemAnimator. Here, we focus on processAdapterUpdatesAndSetAnimationFlags inside, because the method to calculate the mRunSimpleAnimations and mRunPredictiveAnimations.

    private void processAdapterUpdatesAndSetAnimationFlags() {
        if (mDataSetHasChangedAfterLayout) {
            // Processing these items have no value since data set changed unexpectedly.
            // Instead, we just reset it.
            mAdapterHelper.reset();
            if (mDispatchItemsChangedEvent) {
                mLayout.onItemsChanged(this);
            }
        }
        // simple animations are a subset of advanced animations (which will cause a
        // pre-layout step)
        // If layout supports predictive animations, pre-process to decide if we want to run them
        if (predictiveItemAnimationsEnabled()) {
            mAdapterHelper.preProcess();
        } else{ mAdapterHelper.consumeUpdatesInOnePass(); } boolean animationTypeSupported = mItemsAddedOrRemoved || mItemsChanged; mState.mRunSimpleAnimations = mFirstLayoutComplete && mItemAnimator ! = null && (mDataSetHasChangedAfterLayout || animationTypeSupported || mLayout.mRequestedSimpleAnimations) && (! mDataSetHasChangedAfterLayout || mAdapter.hasStableIds()); mState.mRunPredictiveAnimations = mState.mRunSimpleAnimations && animationTypeSupported && ! mDataSetHasChangedAfterLayout && predictiveItemAnimationsEnabled(); }Copy the code

Here we are focusing on the mFirstLayoutComplete variable. We can see that mRunSimpleAnimations have a value associated with mFirstLayoutComplete, MRunPredictiveAnimations are also related to mRunSimpleAnimations. So here we can conclude that when the RecyclerView loads data for the first time, it will not execute the animation. In other words, each ItemView has not finished layout, how can animation. And we can also prove that in a Demo, which we won’t show here.

Now let’s look at the dispatchLayoutStep2 method, which is really laying out the children. Let’s take a look:

    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 disableitem animations; re-check mState.mRunSimpleAnimations = mState.mRunSimpleAnimations && mItemAnimator ! = null; mState.mLayoutStep = State.STEP_ANIMATIONS; onExitLayoutOrScroll(); stopInterceptRequestLayout(false);
    }
Copy the code

Here, let’s focus on two lines of code. One is that here we see the Adapter getItemCount method called; The second is to call the onLayoutChildren method of LayoutManager, which is used to measure and layout children, and this method is also the focus of analysis here.

The onLayoutChildren method of the system’s LayoutManager is an empty method, so a subclass of LayoutManager needs to implement it itself. So from here, we can get two points.

  1. A subclassLayoutManagerYou need to implement it yourselfonLayoutChildrenMethods to determineRecyclerViewIn theLayoutManagerHow to lay it out. And from here, we can see thatRecyclerViewFlexibility.
  2. LayoutManagerSimilar to theViewGroupThat will beonLayoutChildrenMethods (ViewGroupisonLayoutThis pattern is common in Android.

Here, I will not expand the onLayoutChildren method, and I will analyze it in detail later.

Next, let’s analyze the third case — automatic measurement is not turned on.

(3) automatic measurement is not enabled

So let’s look at this piece of code.

            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(); }else {
                mState.mItemCount = 0;
            }
            startInterceptRequestLayout();
            mLayout.onMeasure(mRecycler, mState, widthSpec, heightSpec);
            stopInterceptRequestLayout(false);
            mState.mInPreLayout = false; // clear
Copy the code

For example, in the code above, I’ll divide it into 2 steps:

  1. ifmHasFixedSizeIs true(that is, calledsetHasFixedSizeMethod), will be called directlyLayoutManagertheonMeasureMethods To measure.
  2. ifmHasFixedSizeIs false, and if there is data update, the transaction for data update is processed first and then calledLayoutManagertheonMeasureMethods to measure

From the above description, we know that if automatic measurement is not enabled, the onMeasure method of LayoutManager will be called for measurement, which is the function of the onMeasure method of LayoutManager.

How the onMeasure method is measured depends on the implementation class of LayoutManager. We don’t want to go too far here.

3. layout

Measure process analysis is almost done. Next, we should analyze the second process -layout. Let’s look at the onLayout method:

    @Override
    protected void onLayout(boolean changed, int l, int t, int r, int b) {
        TraceCompat.beginSection(TRACE_ON_LAYOUT_TAG);
        dispatchLayout();
        TraceCompat.endSection();
        mFirstLayoutComplete = true;
    }
Copy the code

The onLayout method doesn’t do much by itself. The emphasis is on the dispatchLayout method.

    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();
            mLayout.setExactMeasureSpecsFrom(this);
            dispatchLayoutStep2();
        } else if(mAdapterHelper.hasUpdates() || mLayout.getWidth() ! = getWidth() || mLayout.getHeight() ! = getHeight()) { // First 2 steps aredone 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();
    }
Copy the code

DispatchLayout method is also very simple, this method to ensure that RecyclerView must go through three processes –dispatchLayoutStep1, dispatchLayoutStep2, dispatchLayoutStep3.

At the same time, in later articles, you will see that the dispatchLayout method actually saves a lot of steps for RecyclerView, that is, after the RecyclerView has undergone a complete dispatchLayout, if the parameters are changed later, You may only go through the last step or two. Of course, these are future stories 馃槀.

For the dispatchLayoutStep1 and dispatchLayoutStep2 methods, we have explained before, there is not too much explanation. Here, let’s briefly look at the dispatchLayoutStep3 method.

    private void dispatchLayoutStep3() {// 路路路路路路 路路路路路路 mstate.mlayoutStep = state.step_start; / /......}Copy the code

Why is this just a quick look at the dispatchLayoutStep3 method? Since this method mainly does the animation of Item, also known as the execution of ItemAnimator, this article will not expand the animation, so we omit the animation part first.

The dispatchLayoutStep3 method we need to focus on here is that it resets mLayoutStep to state.step_start. That is to say, if the next time you start dispatchLayout again, you will definitely experience dispatchLayoutStep1, dispatchLayoutStep2, dispatchLayoutStep3.

The above is the RecyclerView layout process, is it very simple? The difference between RecyclerView and other viewgroups is that if automatic measurement is enabled, the Children layout has been completed in the measure stage. If automatic measurement is not enabled, Children are only laid out in the Layout phase.

4. draw

Next, let’s analyze the final stage of the three processes, CALLED DRAW. Before the formal analysis of draw process, I first to RecyclerView draw to do an overview.

RecyclerView is divided into three steps, let’s have a look:

  1. callsuper.drawMethods. There are two main things done here: 1ChildrenThe draw is distributed toViewGroup; 2. Distribute the dividing line drawing toItemDecoration.
  2. Call if neededItemDecorationtheonDrawOverMethods. Through this method, we are in eachItemViewThere’s a lot of stuff on it.
  3. ifRecyclerViewCall thesetClipToPadding, will achieve a special sliding effect —Each ItemView can slide into the padding area.

Let’s look at this part of the code:

Public void draw(Canvas c) {// first super.draw(c); // final int count = mitemcand.size ();for(int i = 0; i < count; i++) { mItemDecorations.get(i).onDrawOver(c, this, mState); TODO If padding is not 0 and clipChildrenToPadding isfalse, to draw glows properly, we
        // need find children closest to edges. Not sure ifIt is worth the effort. //...Copy the code

If you are familiar with the three processes, you must know that the first step will be called back to the onDraw method. In other words, the drawing of Children and ItemDecoration will be done in the onDraw method.

    @Override
    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); }}Copy the code

Isn’t the onDraw method very simple? Call the super.onDraw method to distribute the drawing of Children to the ViewGroup; Then distribute the drawing of ItemDecoration to the onDraw method of ItemDecoration. From here, we can see that RecyclerView design is really too flexible!

As for the other two steps are relatively simple, I will not analyze them in detail here. The onDraw method of ItemDecoration differs from the onDrawOver method.

5. The onLayoutChildren method of LayoutManager

Generally speaking, the three RecyclerView processes are relatively simple, but in the whole process, we seem to ignore a process — that is, how is RecyclerView layout children?

Before the introduction of dispatchLayoutStep2 method, just a simple introduction, RecyclerView by calling LayoutManager onLayoutChildren method. The LayoutManager itself doesn’t implement this method, so you have to look at its subclasses, so here we’ll look at the LinearLayoutManager.

Since the onLayoutChildren method of LinearLayoutManager is quite long, it is impossible to post the complete code here, so HERE I will give a brief overview of the method for your understanding.

  1. Determine the information of the anchor point, which includes: 1.ChildrenThe layout direction of the start and end direction; 2.mPositionandmCoordinate, respectively representChildrenStart filling position and coordinates.
  2. calldetachAndScrapAttachedViewsMethod,detachOff orremoveoffRecyclerViewtheChildren. This point is outside the scope of this article, but for the sake of a follow-upRecyclerViewThe cache mechanism has a better understanding, here is a special reminder.
  3. Based on the anchor point information, callfillmethodsChildrenThe filling. This process may be called twice, depending on the anchor informationfillMethods.

Next, let’s look at the code:

public void onLayoutChildren(RecyclerView.Recycler recycler, RecyclerView.State state) { // layout algorithm: // 1) by checking children and other variables, find an anchor coordinate and an anchor // item position. // 2) fill towards start, stacking from bottom // 3) fill towards end, (stacking from top // 4) Scroll to fulfill requirements like stack from bottom. // Create layout state // 路路路路路 // Step 1 final View focused = getFocusedChild();if(! mAnchorInfo.mValid || mPendingScrollPosition ! = NO_POSITION || mPendingSavedState ! = null) { mAnchorInfo.reset(); mAnchorInfo.mLayoutFromEnd = mShouldReverseLayout ^ mStackFromEnd; // calculate anchor position and coordinate updateAnchorInfoForLayout(recycler, state, mAnchorInfo); mAnchorInfo.mValid =true; } / /... / / second step detachAndScrapAttachedViews (recycler); mLayoutState.mIsPreLayout = state.isPreLayout(); / / the third stepif (mAnchorInfo.mLayoutFromEnd) {
            // fill towards start
            updateLayoutStateToFillStart(mAnchorInfo);
            mLayoutState.mExtra = extraForStart;
            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;
            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; }} //...Copy the code

Believe that from the above code can find out the implementation of each step. Now, let’s analyze each step in detail. Let’s start with the first step — determining the anchor information.

If you want to see the anchor the calculation process of information, we can from inside updateAnchorInfoForLayout method to find out, let’s take a look at updateAnchorInfoForLayout method:

private void updateAnchorInfoForLayout(RecyclerView.Recycler recycler, RecyclerView.State state, AnchorInfo AnchorInfo) {// The first calculation methodif (updateAnchorFromPendingData(state, anchorInfo)) {
            return; } // The second calculation methodif (updateAnchorFromChildren(recycler, state, anchorInfo)) {
            return; } / / the third calculation anchorInfo assignCoordinateFromPadding (); anchorInfo.mPosition = mStackFromEnd ? state.getItemCount() - 1 : 0; }Copy the code

I believe that through the above code comments, everyone can understand what did updateAnchorInfoForLayout method, here I do simple analysis about the three determine meaning, specific do, is not discussed here, because here are too many details, Deep discussions tend to confuse our brilliant minds 馃槀.

  1. The first method of calculation has two meanings: 1.RecyclerViewIt was rebuilt. It was called backonSaveInstanceStateMethod, so the purpose is to restore the last layout; 2.RecyclerViewCall thescrollToPositionOr something like that, so the goal is to makeRecyclerViewRoll to the right position. Therefore, the anchor information is calculated according to the above two cases.
  2. The second way to calculate it is fromChildrenAbove to calculate the anchor point information. This calculation method also has two cases: 1. If the current has a focusChild, then have the position of the currently focused Child to calculate the anchor point; 2. If no child has the focus, then the layout direction is set bymLayoutFromEndTo determine) get the first visible oneItemViewOr the last oneItemView.
  3. If the first two methods fail, the third method is used, which is the default method.

Do is updateAnchorInfoForLayout methods mentioned above, here is not detailed about the details of each calculation, interested students can have a look.

For the second step, call detachAndScrapAttachedViews method to recycle all ItemView, this part of contents belong to the part of RecyclerView caching mechanism, this paper first buried a foreshadowing here, Follow up to explain RecyclerView will be detailed analysis of it, so here will not explain.

Let’s look at the third step, which is to call the fill method to populate Children. To formally analyze the filling process, let’s start with a picture:

RecyclerView analysis

The figure above shows three cases of fill. Where we can see the third case, where the fill method is called twice.

Let’s look at the fill method:

int fill(RecyclerView.Recycler recycler, LayoutState layoutState, RecyclerView.State state, Boolean stopOnFocusable) {// 路路路路while((layoutState mInfinite | | remainingSpace > 0) && layoutState. HasMore (state)) {/ / 路 路 路 路 路 路 layoutChunk (recycler, state, layoutState, layoutChunkResult); } / /......}Copy the code

The fill method is a long code that actually calculates the amount of space that can be filled. The actual place to fill the Child is the layoutChunk method. Let’s look at the layoutChunk method.

Because the layoutChunk method is quite long, I will not show it completely here. For easy understanding, I will make a brief overview of this method to give you a general understanding.

  1. callLayoutStatethenextMethod to obtain aItemView. Don’t underestimate thisnextMethod,RecyclerViewThe starting point for caching is this method, and you can imagine how much this method has done for us.
  2. ifRecyclerViewIs the first layout of Children’s words (layoutState.mScrapList == nullTrue), will call addView first, willViewAdded to theRecyclerViewGo inside.
  3. callmeasureChildWithMarginsMethod, measure eachItemViewThe width of high. Note that this method measures the width and height of the ItemView considering two factors: 1. 2.ItemDecorationtheoffset.
  4. calllayoutDecoratedWithMarginsMethod, layoutItemView. These two factors are also considered here.

As for each step of the specific dry, here is not detailed explanation, are some basic operations, interested students can have a look.

To sum up, the whole execution process of onLayoutChildren method of LayoutManager is relatively simple.

6. Summary

That’s all for this article, and I’ll make a brief summary at the end.

  1. RecyclerViewthemeasureThe process is divided into three cases, each of which has an execution process. In general, we go through an automatic measurement process.
  2. Automatic measurement needs to be separatedmState.mLayoutStepState value, because different values are called based on different state valuesdispatchLayoutStepMethods.
  3. layoutThe process is also based onmState.mLayoutStepState to call differentdispatchLayoutStepmethods
  4. drawThe process mainly does four things: 1ItemDecorationtheonDrawPart; 2. DrawChildren; 3. DrawItemDecorationthedrawOverPart; 4. According tomClipToPaddingTo determine whether a special drawing is performed.