当前位置:网站首页>How are items filled or recycled when scrolling?

How are items filled or recycled when scrolling?

2020-12-07 21:16:55 Tang Zixuan

It's another story about RecyclerView Interview questions :“RecyclerView Rolling time , How new entries are filled in one by one ? How old entries are recycled one by one ?” This article to read the source code way , Answer this question .

This is a RecyclerView The ninth in the interview series , The contents of the series are as follows :

  1. RecyclerView Caching mechanisms | How to reuse table entries ?

  2. RecyclerView Caching mechanisms | What to recycle ?

  3. RecyclerView Caching mechanisms | Where to recycle ?

  4. RecyclerView Caching mechanisms | scrap view Life cycle of

  5. RecyclerView Animation principle | pre-layout,post-layout And scrap Cache relationship

  6. RecyclerView Animation principle | Change the posture to see the source code (pre-layout)

  7. RecyclerView Animation principle | pre-layout,post-layout And scrap Cache relationship

  8. RecyclerView Animation principle | How to store and apply animation property values ?

  9. RecyclerView Interview questions | When the list scrolls , How entries are filled or recycled ?

The source of the trigger roll

Fingers slide on the screen , The list then scrolls , The source of triggering scrolling must be in the touch event :

public class RecyclerView {
    // RecyclerView  heavy load  onTouchEvent()
    @Override
    public boolean onTouchEvent(MotionEvent e) {
        switch (action) {
            // RecyclerView  Handling of sliding Events 
            case MotionEvent.ACTION_MOVE: {
                ...
                if (scrollByInternal(
                        canScrollHorizontally ? dx : 0,
                        canScrollVertically ? dy : 0,
                        e)) {...}
            }
        }
    }
}
 Copy code 

RecyclerView Called when handling the sliding event scrollByInternal(), And the rolling displacement is passed in as a parameter :

public class RecyclerView {
    boolean scrollByInternal(int x, int y, MotionEvent ev) {
        ...
        scrollStep(x, y, mReusableIntPair);
        ...
        //  Really rolling 
        dispatchNestedScroll(consumedX, consumedY, unconsumedX, unconsumedY, mScrollOffset,TYPE_TOUCH, mReusableIntPair);
        ...
    }
}
 Copy code 

Before you actually roll , Called scrollStep(), The displacement continues to pass as a parameter :

public class RecyclerView {
    LayoutManager mLayout;
    void scrollStep(int dx, int dy, @Nullable int[] consumed) {
        ...
        if (dx != 0) {
            consumedX = mLayout.scrollHorizontallyBy(dx, mRecycler, mState);
        }
        if (dy != 0) {
            consumedY = mLayout.scrollVerticallyBy(dy, mRecycler, mState);
        }
        ...
    }
}
 Copy code 

scrollStep() It deals with rolling in two directions respectively , And entrusted it to LayoutManager, With LinearLayoutManager For example, vertical scrolling in :

public class LinearLayoutManager {
    @Override
    public int scrollVerticallyBy(int dy, RecyclerView.Recycler recycler, RecyclerView.State state) {
        if (mOrientation == HORIZONTAL) { return 0; }
        return scrollBy(dy, recycler, state);
    }
}
 Copy code 

The vertical displacement is passed in as a parameter , And pass it on to scrollBy():

public class LinearLayoutManager {
    int scrollBy(int delta, RecyclerView.Recycler recycler, RecyclerView.State state) {
        ...
        //  Fill in the table entry 
        final int consumed = mLayoutState.mScrollingOffset + fill(recycler, mLayoutState, state, false);
        ...
    }
}
 Copy code 

A key method has been found fill(), Look at the name “ fill ” It means , Is it possible to fill in the upcoming entries before scrolling ?

Fill in the table entry

With questions , It opens at fill()

public class LinearLayoutManager {
    //  Fill in the table items according to the remaining space 
    int fill(RecyclerView.Recycler recycler, LayoutState layoutState,RecyclerView.State state, boolean stopOnFocusable) {
        ...
        //  Calculate the remaining space  =  Available space  +  Extra space (=0)
        int remainingSpace = layoutState.mAvailable + layoutState.mExtraFillSpace;
        //  loop , When the remaining space  > 0  when , Continue to fill in more entries 
        while ((layoutState.mInfinite || remainingSpace > 0) && layoutState.hasMore(state)) {
            ...
            //  Fill in a single entry 
            layoutChunk(recycler, state, layoutState, layoutChunkResult)
            ...
        }
    }
}
 Copy code 

Filling in a table entry is a while loop , The end condition of the loop is “ Whether the remaining space in the list is > 0”, Every time the loop calls layoutChunk() Fill in the list with a single table item :

public class LinearLayoutManager {
    //  Fill in a single entry 
    void layoutChunk(RecyclerView.Recycler recycler, RecyclerView.State state,LayoutState layoutState, LayoutChunkResult result) {
        // 1. Get the next table item view to be populated 
        View view = layoutState.next(recycler);
        // 2. Make the entry  RecyclerView  The child views 
        addView(view);
        ...
        // 3. Measurement table item view ( hold  RecyclerView  The inner margin and item decoration are taken into account )
        measureChildWithMargins(view, 0, 0);
        //  Get the pixel value consumed to fill the table item view 
        result.mConsumed = mOrientationHelper.getDecoratedMeasurement(view);
        ...
        // 4. Layout table items 
        layoutDecoratedWithMargins(view, left, top, right, bottom);
    }
}
 Copy code 

layoutChunk() First get the next view from the cache pool that should be filled with new table entries , It's called a new entry , It's because before the rollover happens , These entries are not yet displayed on the screen .( The detailed analysis of reuse can be moved to RecyclerView Caching mechanisms | How to reuse table entries ?).

Then I call addView() Make the table item view RecyclerView The child views , The call chain is as follows :

public class RecyclerView {
    ChildHelper mChildHelper;
    public abstract static class LayoutManager {
        public void addView(View child) {
            addView(child, -1);
        }
        
        public void addView(View child, int index) {
            addViewInt(child, index, false);
        }
        
        private void addViewInt(View child, int index, boolean disappearing) {
            ...
            //  Entrusted to  ChildHelper
            mChildHelper.attachViewToParent(child, index, child.getLayoutParams(), false);
            ...
        }
    }
}

class ChildHelper {
    final Callback mCallback;
    void attachViewToParent(View child, int index, ViewGroup.LayoutParams layoutParams,boolean hidden) {
        ...
        mCallback.attachViewToParent(child, offset, layoutParams);
    }
}
 Copy code 

Call chain from RecyclerView To LayoutManager Until then ChildHelper, And finally back RecyclerView

public class RecyclerView {
    ChildHelper mChildHelper;
    private void initChildrenHelper() {
        mChildHelper = new ChildHelper(new ChildHelper.Callback() {
            @Override
            public void attachViewToParent(View child, int index,ViewGroup.LayoutParams layoutParams) {
                ...
                RecyclerView.this.attachViewToParent(child, index, layoutParams);
            }
            ...
        }
    }
}
 Copy code 

addView() The ultimate goal of this project is ViewGroup.attachViewToParent()

public abstract class ViewGroup {
    protected void attachViewToParent(View child, int index, LayoutParams params) {
        ...
        //  Add a subview to the array 
        addInArray(child, index);
        //  The child view is associated with the father 
        child.mParent = this;
        ...
    }
}
 Copy code 

attachViewToParent() It contains “ Add subviews ” The two most iconic moves :1. Add a subview to the array 2. The child view is associated with the father .

The entry becomes RecyclerView After the subview , It was measured :

public class LinearLayoutManager {
    //  Fill in a single entry 
    void layoutChunk(RecyclerView.Recycler recycler, RecyclerView.State state,LayoutState layoutState, LayoutChunkResult result) {
        // 1. Get the next table item view to be populated 
        View view = layoutState.next(recycler);
        // 2. Make the entry  RecyclerView  The child views 
        addView(view);
        ...
        // 3. Measurement table item view ( hold  RecyclerView  The inner margin and item decoration are taken into account )
        measureChildWithMargins(view, 0, 0);
        //  Get the pixel value consumed to fill the table item view 
        result.mConsumed = mOrientationHelper.getDecoratedMeasurement(view);
        ...
        // 4. Layout table items 
        layoutDecoratedWithMargins(view, left, top, right, bottom);
    }
}
 Copy code 

Measure the size of the subview , You can know how many pixel values will be consumed to fill the table entry , Store the value in LayoutChunkResult.mConsumed in .

When you have the size , So you can lay out the table items , That is to make sure that the four points above, below, left and right of the table item are relative to RecyclerView The location of :

public class RecyclerView {
    public abstract static class LayoutManager {
        public void layoutDecoratedWithMargins(View child, int left, int top, int right,int bottom) {
            final LayoutParams lp = (LayoutParams) child.getLayoutParams();
            final Rect insets = lp.mDecorInsets;
            //  Locate the child table entry 
            child.layout(left + insets.left + lp.leftMargin, top + insets.top + lp.topMargin,
                    right - insets.right - lp.rightMargin,
                    bottom - insets.bottom - lp.bottomMargin);
        }
    }
}
 Copy code 

Call the control's layout() Method is to locate the control , For the detailed introduction of positioning child control, you can move Android Custom control | View Drawing principle ( Where is the painting ?).

After filling in an entry , From remainingSpace Deduct the space it takes up ( such while The cycle ends )

public class LinearLayoutManager {
    //  Fill in the table items according to the remaining space 
    int fill(RecyclerView.Recycler recycler, LayoutState layoutState,RecyclerView.State state, boolean stopOnFocusable) {
        ...
        //  Calculate the remaining space  =  Available space  +  Extra space (=0)
        int remainingSpace = layoutState.mAvailable + layoutState.mExtraFillSpace;
        //  loop , When the remaining space  > 0  when , Continue to fill in more entries 
        while ((layoutState.mInfinite || remainingSpace > 0) && layoutState.hasMore(state)) {
            ...
            //  Fill in a single entry 
            layoutChunk(recycler, state, layoutState, layoutChunkResult)
            ...
            //  Subtract the pixel value of the new entry from the remaining space 
            layoutState.mAvailable -= layoutChunkResult.mConsumed;
            remainingSpace -= layoutChunkResult.mConsumed;
            ...
        }
    }
}
 Copy code 

So we can draw a conclusion :

  1. RecyclerView Before scrolling occurs , There will be an action to fill in a new entry , Fill in the table items that are not currently displayed .

  2. RecyclerView Fill in the entries by while Circularly implemented , When there is no space left , Fill in the entries and it's over .

How many new entries should be filled in ? Take a look back while The exit condition of the cycle :

public class LinearLayoutManager {
    //  Fill in the table items according to the remaining space 
    int fill(RecyclerView.Recycler recycler, LayoutState layoutState,RecyclerView.State state, boolean stopOnFocusable) {
        ...
        //  Calculate the remaining space  =  Available space  +  Extra space (=0)
        int remainingSpace = layoutState.mAvailable + layoutState.mExtraFillSpace;
        //  loop , When the remaining space  > 0  when , Continue to fill in more entries 
        while ((layoutState.mInfinite || remainingSpace > 0) && layoutState.hasMore(state)) {...}
    }
}
 Copy code 

The number of entries to fill in the table depends on remainingSpace Size , Its value is the sum of two variables , among layoutState.mExtraFillSpace The value of is 0( Breakpoint debugging tells me ), and layoutState.mAvailable It's the input parameter layoutState Decisive , Search the web of the call chain where it is assigned :

public class LinearLayoutManager {
    int scrollBy(int delta, RecyclerView.Recycler recycler, RecyclerView.State state) {
        ...
        //  Take the absolute value of sliding displacement 
        final int absDelta = Math.abs(delta);
        //  to update  LayoutState ( The absolute value of displacement is passed in )
        updateLayoutState(layoutDirection, absDelta, true, state);
        //  Fill in the table entry 
        final int consumed = mLayoutState.mScrollingOffset + fill(recycler, mLayoutState, state, false);
        ...
    }

    private void updateLayoutState(int layoutDirection, int requiredSpace, boolean canUseExistingSpace, RecyclerView.State state) {
        ...
        mLayoutState.mAvailable = requiredSpace;
        ...
    }
}
 Copy code 

Before filling in the table entry ,mLayoutState.mAvailable The value of is set to the absolute value of the rolling displacement .

At this point, we can further refine the previous conclusion :

RecyclerView Before scrolling occurs , The number of new entries to be filled in the list is determined by the size of the scrolling displacement .

Reclaim entries

There are new entries filled into the list , The old entries are recycled , It's like rolling , New entries move to the screen , The old entry moves out of the screen .

How do you decide which items to recycle ?

RecyclerView adopt Recycler.recycleView() Reclaim entries , Take it as a starting point , Look up the call chain to see if there is a place related to scrolling :

public class RecyclerView {
    public final class Recycler {
        // 0
        public void recycleView(@NonNull View view) {...}
    }
    
    public abstract static class LayoutManager {
        public void removeAndRecycleViewAt(int index, @NonNull Recycler recycler) {
            final View view = getChildAt(index);
            removeViewAt(index);
            // 1
            recycler.recycleView(view);
        }
    }
}

public class LinearLayoutManager {
    private void recycleChildren(RecyclerView.Recycler recycler, int startIndex, int endIndex) {
        // 2: The recycling index value is  endIndex -1  To  startIndex  Table items of 
        for (int i = endIndex - 1; i >= startIndex; i--) {
            removeAndRecycleViewAt(i, recycler);
        }
    }
    
    private void recycleViewsFromStart(RecyclerView.Recycler recycler, int scrollingOffset,int noRecycleSpace) {
        ...
        for (int i = 0; i < childCount; i++) {
            View child = getChildAt(i);
            if (mOrientationHelper.getDecoratedEnd(child) > limit|| mOrientationHelper.getTransformedEndWithDecoration(child) > limit) {
                // 3
                recycleChildren(recycler, 0, i);
            }
        }
    }
    
    private void recycleByLayoutState(RecyclerView.Recycler recycler, LayoutState layoutState) {
        // 4
        recycleViewsFromStart(recycler, scrollingOffset, noRecycleSpace);
    }
    
    int fill(RecyclerView.Recycler recycler, LayoutState layoutState,RecyclerView.State state, boolean stopOnFocusable) {
        ...
        //  Loop in the table entry 
        while ((layoutState.mInfinite || remainingSpace > 0) && layoutState.hasMore(state)) {
            //  Fill in a single entry 
            layoutChunk(recycler, state, layoutState, layoutChunkResult);
            ...
            if (layoutState.mScrollingOffset != LayoutState.SCROLLING_OFFSET_NaN) {
                layoutState.mScrollingOffset += layoutChunkResult.mConsumed;
                // 5: Reclaim entries 
                recycleByLayoutState(recycler, layoutState);
            }
            ...
        }
    }
}
 Copy code 

Go all the way up the call chain ( In comments 0-5), I'm actually filling in the table fill() The operation of reclaiming table items was found in method . And after each cycle fills in a new entry , The recycling operation was carried out immediately .

Which items are recycled ?

Answer that question , In the code just now recycleChildren(recycler, 0, i) The outside logic of judgment is the key :

public class LinearLayoutManager {
    private void recycleViewsFromStart(RecyclerView.Recycler recycler, int scrollingOffset,int noRecycleSpace) {
        ...
        //  Traverses all current table entries in the list 
        for (int i = 0; i < childCount; i++) {
            View child = getChildAt(i);
            //  If the sub table item satisfies a certain condition , Then reclaim the index from  0  To  i-1  Table items of 
            if (mOrientationHelper.getDecoratedEnd(child) > limit|| mOrientationHelper.getTransformedEndWithDecoration(child) > limit) {
                recycleChildren(recycler, 0, i);
            }
        }
    }
}
 Copy code 

The key judgment condition of recovery items is mOrientationHelper.getDecoratedEnd(child) > limit.

Among them mOrientationHelper.getDecoratedEnd(child) The code is as follows :

//  Abstract interface of shielding direction , Used to reduce the direction of  if-else
public abstract class OrientationHelper {
    //  Gets the distance of the current table item relative to the top of the list 
    public abstract int getDecoratedEnd(View view);
    //  Vertical layout of the interface implementation 
    public static OrientationHelper createVerticalHelper(RecyclerView.LayoutManager layoutManager) {
        return new OrientationHelper(layoutManager) {
            @Override
            public int getDecoratedEnd(View view) {
                final RecyclerView.LayoutParams params = (RecyclerView.LayoutParams)view.getLayoutParams();
                return mLayoutManager.getDecoratedBottom(view) + params.bottomMargin;
            }
}
 Copy code 

mOrientationHelper.getDecoratedEnd(child) Represents the distance between the bottom of the current table item and the top of the list ,OrientationHelper This layer of abstraction blocks the direction of the list , So this sentence in the vertical list can be translated into “ The ordinate of the bottom of the current table item relative to the top of the list ”.

Judge the condition mOrientationHelper.getDecoratedEnd(child) > limit Medium limit What do you mean ?

In the vertical list ,“ The ordinate of the bottom of the table item > A certain value ” It means that the table item is below a line , namely limit It's the invisible line in the list , All entries above this line should be recycled .

So this one limit Invisible line How is it calculated ?

public class LinearLayoutManager extends RecyclerView.LayoutManager {
    private void recycleViewsFromStart(RecyclerView.Recycler recycler, int scrollingOffset,int noRecycleSpace) {
        //  Calculate the value of the invisible line 
        final int limit = scrollingOffset - noRecycleSpace;
        ...
        for (int i = 0; i < childCount; i++) {
            View child = getChildAt(i);
            //  If the sub table item satisfies a certain condition , Then reclaim the index from  0  To  i  Table items of 
            if (mOrientationHelper.getDecoratedEnd(child) > limit|| mOrientationHelper.getTransformedEndWithDecoration(child) > limit) {
                recycleChildren(recycler, 0, i);
            }
        }
    }
}
 Copy code 

limit The value of is determined by 2 Variables determine , among noRecycleSpace The value of is 0( This is what the breakpoint tells me , The detailed process can be changed RecyclerView Animation principle | Change the posture to see the source code (pre-layout)

and scrollingOffset The value of is passed in from the outside :

public class LinearLayoutManager {
    private void recycleByLayoutState(RecyclerView.Recycler recycler, LayoutState layoutState) {
        int scrollingOffset = layoutState.mScrollingOffset;
        ...
        recycleViewsFromStart(recycler, scrollingOffset, noRecycleSpace);
    }
}
 Copy code 

limit The value of is layoutState.mScrollingOffset Value , The question turned to layoutState.mScrollingOffset What determines the value of ? Global search where it is assigned :

public class LinearLayoutManager {
    private void updateLayoutState(int layoutDirection, int requiredSpace,boolean canUseExistingSpace, RecyclerView.State state) {
        ...
        int scrollingOffset;
        //  Get the table item view at the end of the list 
        final View child = getChildClosestToEnd();
        //  Calculation without filling the list with new entries , The maximum number of pixels the list can scroll through 
        scrollingOffset = mOrientationHelper.getDecoratedEnd(child) - mOrientationHelper.getEndAfterPadding();
        ...
        // mLayoutState.mScrollingOffset  Assigned 
        mLayoutState.mScrollingOffset = scrollingOffset;
    }
    
    int scrollBy(int delta, RecyclerView.Recycler recycler, RecyclerView.State state) {
        ...
        //  Absolute value of sliding displacement 
        final int absDelta = Math.abs(delta);
        //  to update  LayoutState
        updateLayoutState(layoutDirection, absDelta, true, state);
        //  Before sliding , Fill in a new entry , Reclaim old entries 
        final int consumed = mLayoutState.mScrollingOffset + fill(recycler, mLayoutState, state, false);
        ...
    }
}
 Copy code 

Before the slide occurs and you're ready to fill in a new entry , Called updateLayoutState(), This method first obtains the view of the table item at the end of the list , And pass mOrientationHelper.getDecoratedEnd(child) Calculate the distance from the bottom of the item to the top of the list , And then subtract the length of the list . This difference can be interpreted as Without filling the list with new entries , The maximum number of pixels the list can scroll through . A little abstract , Here is the following :

The blue border in the figure indicates the list , The gray rectangle represents the table item .

LayoutManager Only visible table entries will be loaded , The table in the figure 6 Half of it's on the screen , So it's loaded into the list , Completely invisible entries 7 Will not be loaded . In this case , If you do not continue to fill the list with entries 7, The maximum sliding distance of the list is half an entry 6 The length of , In the code, that is mLayoutState.mScrollingOffset Value .

Suppose the table entry 6 There's no more data after that , That is, the list can only slide to the table item 6 The bottom of . In this scenario limit Value = Half of the entries 6 The length of . in other words limit Invisible line It should be in the following position :

Take a look back , Code of recycling table item :

public class LinearLayoutManager {
    private void recycleViewsFromStart(RecyclerView.Recycler recycler, int scrollingOffset,int noRecycleSpace) {
        final int limit = scrollingOffset - noRecycleSpace;
        // Traverse from the beginning  LinearLayoutManager, To find out which entries should be recycled 
        final int childCount = getChildCount();
        for (int i = 0; i < childCount; i++) {
            View child = getChildAt(i);
            //  If the lower boundary of a table entry  > limit  Invisible line 
            if (mOrientationHelper.getDecoratedEnd(child) > limit
                    || mOrientationHelper.getTransformedEndWithDecoration(child) > limit) {
                // The recycling index is  0  To  i-1  Table items of 
                recycleChildren(recycler, 0, i);
                return;
            }
        }
    }
}
 Copy code 

Recycle logic traverses from scratch LinearLayoutManager, When traversing to the table entry 1 When , Find its lower boundary > limit, So trigger item recycling , The index range of recovery table items is 0 To 0 - 1, That is, no items are recycled .( Wanting is also , Table item 1 Not completely removed from the screen ).

So we can draw a conclusion :

RecyclerView Before sliding , The accountant worked out a rule limit Invisible line , It is an important basis for determining which items should be recycled . When the recycle logic is triggered , Will traverse all current table entries , If the bottom of an entry is at limit Below the invisible line , All items above the entry are recycled .

Make the scenario more general , If the table entry 6 And then there's the data , And what happens when the sliding distance is large ?

Calculation limit The method of value updateLayoutState() stay scrollBy() In the called :

public class LinearLayoutManager {
    int scrollBy(int delta, RecyclerView.Recycler recycler, RecyclerView.State state) {
        ...
        //  Pass the absolute value of the rolling distance into  updateLayoutState()
        final int absDelta = Math.abs(delta);
        updateLayoutState(layoutDirection, absDelta, true, state);
        ...
    }
    
    private void updateLayoutState(int layoutDirection, int requiredSpace,boolean canUseExistingSpace, RecyclerView.State state) {
        ...
        //  Calculation without filling the list with new entries , The maximum number of pixels the list can scroll through 
        scrollingOffset = mOrientationHelper.getDecoratedEnd(child)- mOrientationHelper.getEndAfterPadding();
        ...
        //  Store the extra space needed for scrolling the list in  mLayoutState.mAvailable
        mLayoutState.mAvailable = requiredSpace;
        mLayoutState.mScrollingOffset = scrollingOffset;
        ...
    }
}
 Copy code 

Two important values are stored in sequence in mLayoutState.mScrollingOffset and mLayoutState.mAvailable, Namely “ Without filling the list with new entries , The maximum number of pixels the list can scroll through ”, And “ Expected scrolling pixel value ”. The former is the basis for how many old entries are recovered , The latter is the basis for how many new entries are filled in .

srollBy() Calling updateLayoutState() After storing these two important values , Immediately, the operation of filling the table items is carried out :

public class LinearLayoutManager {
    int scrollBy(int delta, RecyclerView.Recycler recycler, RecyclerView.State state) {
        ...
        final int absDelta = Math.abs(delta);
        updateLayoutState(layoutDirection, absDelta, true, state);
        //  Fill in the table entry 
        final int consumed = mLayoutState.mScrollingOffset + fill(recycler, mLayoutState, state, false);
        ...
    }
}
 Copy code 

Storing two important values mLayoutState Passed in as a parameter fill()

public class LinearLayoutManager {
    int fill(RecyclerView.Recycler recycler, LayoutState layoutState, RecyclerView.State state, boolean stopOnFocusable) {
        ...
        //  Use the estimated slip distance as the basis for how many new entries to fill in 
        int remainingSpace = layoutState.mAvailable + layoutState.mExtraFillSpace;
        while ((layoutState.mInfinite || remainingSpace > 0) && layoutState.hasMore(state)) {
            //  Fill in a single entry 
            layoutChunk(recycler, state, layoutState, layoutChunkResult);
            ...
            //  stay  layoutState.mScrollingOffset  Add the pixel value consumed due to the new table item filling 
            layoutState.mScrollingOffset += layoutChunkResult.mConsumed;
            //  Reclaim entries 
            recycleByLayoutState(recycler, layoutState);
            ...
        }
        ...
    }
}
 Copy code 

When a new table entry is filled in a loop , The pixel value occupied by the new table item is appended to layoutState.mScrollingOffset, That is, its value is increasing (limit Invisible line It's moving down ). In a while At the end of the cycle , Would call recycleByLayoutState() Based on the current limit Invisible line The location of the recycle table entry :

public class LinearLayoutManager {
    private void recycleByLayoutState(RecyclerView.Recycler recycler, LayoutState layoutState) {
        ...
        recycleViewsFromStart(recycler, scrollingOffset, noRecycleSpace);
    }
}
    
    private void recycleViewsFromStart(RecyclerView.Recycler recycler, int scrollingOffset,int noRecycleSpace) {
        final int limit = scrollingOffset - noRecycleSpace;
        final int childCount = getChildCount();
        //  Go through the entries from the beginning 
        for (int i = 0; i < childCount; i++) {
            View child = getChildAt(i);
            //  When the bottom of an entry is at  limit  After the invisible line , Recycle all the entries above it 
            if (mOrientationHelper.getDecoratedStart(child) > limit || mOrientationHelper.getTransformedStartWithDecoration(child) > limit) {
                recycleChildren(recycler, 0, i);
                return;
            }
        }
    }
}
 Copy code 

Fill in one entry at the end of the list ,limit Invisible line The pixel value occupied by the table entry is moved down , In this way, there are more entries in the head of the list that are eligible for recycling .

Analysis of recycling details , You can move RecyclerView Caching mechanisms | Where to recycle ?.

Sum up with a picture limit Invisible line ( The red dotted line in the picture ):

limit The value of represents the total distance of this actual roll .( This is an ideal situation , That is, when the scrolling ends, a new entry is inserted 7 The bottom of the list just overlaps the bottom of the list )

limit Invisible line It can be understood as : The current position of the invisible line , After scrolling, it overlaps with the top of the list

summary

  1. RecyclerView Before scrolling occurs , How many new entries need to be filled into the list based on the expected rolling displacement .

  2. RecyclerView Fill in the entries by while Loop one by one , When there is no space left , Fill in the entries and it's over .

  3. RecyclerView Before sliding , The accountant worked out a rule limit Invisible line , It is an important basis for determining which items should be recycled . It can be understood as : The current position of the invisible line , After scrolling, it overlaps with the top of the list

  4. limit Invisible line The initial value of the = The distance from the bottom of the currently visible list item to the top of the list , That is, when the list is not filled with new table items , The maximum distance that can slide . The pixel value consumed by each new fill image is appended to limit Value above , namely limit Invisible line It will continue to move down as new items are filled in .

  5. When the recycle logic is triggered , Will traverse all current table entries , If the bottom of an entry is at limit Invisible line below , All items above the entry are recycled .

Recommended reading

  1. RecyclerView Caching mechanisms | How to reuse table entries ?

  2. RecyclerView Caching mechanisms | What to recycle ?

  3. RecyclerView Caching mechanisms | Where to recycle ?

  4. RecyclerView Caching mechanisms | scrap view Life cycle of

  5. Read source long knowledge | better RecyclerView Click on the monitor

  6. Proxy mode application | Whenever for RecyclerView I'm crazy when I add new types

  7. better RecyclerView Table item child control click listener

  8. Refresh more efficiently RecyclerView | DiffUtil Secondary packaging

  9. Another way of thinking , Super simple RecyclerView Preloading

  10. RecyclerView Animation principle | Change the posture to see the source code (pre-layout)

  11. RecyclerView Animation principle | pre-layout,post-layout And scrap Cache relationship

  12. RecyclerView Animation principle | How to store and apply animation property values ?

  13. RecyclerView Interview questions | When the list scrolls , How entries are filled or recycled ?

版权声明
本文为[Tang Zixuan]所创,转载请带上原文链接,感谢
https://chowdera.com/2020/12/202012071915217978.html