RecyclerView RecyclerView how can new entries be filled in one by one? How are old entries recycled one by one?” This article answers this question by means of walking through the source code.
This is the ninth in the RecyclerView interview series, which includes the following table of contents:
RecyclerView caching mechanism | how to reuse table?
What RecyclerView caching mechanism | recycling?
RecyclerView caching mechanism | recycling where?
RecyclerView caching mechanism | scrap the view of life cycle
RecyclerView animation principle | change the posture to see the source code (pre – layout)
RecyclerView animation principle | pre – layout, post – the relationship between the layout and scrap the cache
RecyclerView animation principle | how to store and use animation attribute values?
RecyclerView list of interview questions | scroll, how the list items are filled or recycled?
RecyclerView interview question | what item in the table below is recycled to the cache pool?
The source that triggers the scroll
When your finger slides on the screen, the list will scroll accordingly, and the source of scrolling must be in the touch event:
public class RecyclerView {
// RecyclerView override onTouchEvent()
@Override
public boolean onTouchEvent(MotionEvent e) {
switch (action) {
// RecyclerView handles sliding events
case MotionEvent.ACTION_MOVE: {
...
if (scrollByInternal(
canScrollHorizontally ? dx : 0,
canScrollVertically ? dy : 0, e)) {... } } } } }Copy the code
The RecyclerView calls scrollByInternal() when it handles the slide event and passes in the roll displacement as an argument:
public class RecyclerView {
boolean scrollByInternal(int x, int y, MotionEvent ev) {... scrollStep(x, y, mReusableIntPair); .// Truly implement scrollingdispatchNestedScroll(consumedX, consumedY, unconsumedX, unconsumedY, mScrollOffset,TYPE_TOUCH, mReusableIntPair); . }}Copy the code
Before scrolling is actually implemented, scrollStep() is called and the shift continues to be passed as an argument:
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 the code
ScrollStep () handles the rolling in both directions and delegates it to the LayoutManager, using vertical rolling in the LinearLayoutManager as an example:
public class LinearLayoutManager {
@Override
public int scrollVerticallyBy(int dy, RecyclerView.Recycler recycler, RecyclerView.State state) {
if (mOrientation == HORIZONTAL) { return 0; }
returnscrollBy(dy, recycler, state); }}Copy the code
The vertical displacement is passed in as a parameter and is passed to scrollBy():
public class LinearLayoutManager {
int scrollBy(int delta, RecyclerView.Recycler recycler, RecyclerView.State state) {...// Fill in the entry
final int consumed = mLayoutState.mScrollingOffset + fill(recycler, mLayoutState, state, false); . }}Copy the code
The key method to fill() is to fill the entry before scrolling through the list.
Fill the table item
Fill () with questions:
public class LinearLayoutManager {
// Fill the entry according to the remaining space
int fill(RecyclerView.Recycler recycler, LayoutState layoutState,RecyclerView.State state, boolean stopOnFocusable) {...// Calculate free space = free space + extra space (=0)
int remainingSpace = layoutState.mAvailable + layoutState.mExtraFillSpace;
// Continue to fill more entries when the remaining space is greater than 0
while ((layoutState.mInfinite || remainingSpace > 0) && layoutState.hasMore(state)) {
...
// Populate a single entrylayoutChunk(recycler, state, layoutState, layoutChunkResult) ... }}}Copy the code
Populating a table entry is a while loop that ends with the condition “is the list free space > 0?”, and each time the loop calls layoutChunk() to populate a single table entry into the list:
public class LinearLayoutManager {
// Populate a single entry
void layoutChunk(RecyclerView.Recycler recycler, RecyclerView.State state,LayoutState layoutState, LayoutChunkResult result) {
// 1. Obtain the next entry view to be populated
View view = layoutState.next(recycler);
// 2. Make the entry a child of RecyclerViewaddView(view); .// 3. Measure table item view (RecyclerView inner margin and table item decoration taken into account)
measureChildWithMargins(view, 0.0);
// Get the number of pixels consumed to populate the entry viewresult.mConsumed = mOrientationHelper.getDecoratedMeasurement(view); .// 4. Layout entrieslayoutDecoratedWithMargins(view, left, top, right, bottom); }}Copy the code
LayoutChunk () first gets the view of the next entry to be populated from the cache pool, which is called the new entry because the entries are not on the screen before scrolling occurs. (detailed analysis about the reuse to the RecyclerView caching mechanism | how to reuse table?) .
Then call addView() to create a RecyclerView child of the RecyclerView. Call chain 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) {...// Delegate 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 the code
The chain of calls goes from RecyclerView to LayoutManager to ChildHelper and back to RecyclerView:
public class RecyclerView {
ChildHelper mChildHelper;
private void initChildrenHelper(a) {
mChildHelper = new ChildHelper(new ChildHelper.Callback() {
@Override
public void attachViewToParent(View child, int index,ViewGroup.LayoutParams layoutParams) {... RecyclerView.this.attachViewToParent(child, index, layoutParams); }... }}}Copy the code
AddView () the final foothold is ViewGroup attachViewToParent () :
public abstract class ViewGroup {
protected void attachViewToParent(View child, int index, LayoutParams params) {...// Add the subview to the array
addInArray(child, index);
// The child view is associated with the parent
child.mParent = this; . }}Copy the code
AttachViewToParent () contains two of the most iconic actions for adding a child view: 1. Add a subview to an array. 2. Associate a subview with its parent.
After the table becomes a RecyclerView subview, it is measured:
public class LinearLayoutManager {
// Populate a single entry
void layoutChunk(RecyclerView.Recycler recycler, RecyclerView.State state,LayoutState layoutState, LayoutChunkResult result) {
// 1. Obtain the next entry view to be populated
View view = layoutState.next(recycler);
// 2. Make the entry a child of RecyclerViewaddView(view); .// 3. Measure table item view (RecyclerView inner margin and table item decoration taken into account)
measureChildWithMargins(view, 0.0);
// Get the number of pixels consumed to populate the entry viewresult.mConsumed = mOrientationHelper.getDecoratedMeasurement(view); .// 4. Layout entrieslayoutDecoratedWithMargins(view, left, top, right, bottom); }}Copy the code
Measuring size of the view, you can know how fill the table item will consume the pixel values, the value stored in the LayoutChunkResult. MConsumed.
After the size, you can also layout the table, that is, determine the table item up and down around four points relative to RecyclerView position:
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 subentrychild.layout(left + insets.left + lp.leftMargin, top + insets.top + lp.topMargin, right - insets.right - lp.rightMargin, bottom - insets.bottom - lp.bottomMargin); }}}Copy the code
Call control layout () method is to control positioning, about positioning child controls detailed introduction to the Android custom controls | View drawing principle (pictures?) .
After you’ve filled in an entry, you deduct the space it takes from the remainingSpace (so the while loop ends)
public class LinearLayoutManager {
// Fill the entry according to the remaining space
int fill(RecyclerView.Recycler recycler, LayoutState layoutState,RecyclerView.State state, boolean stopOnFocusable) {...// Calculate free space = free space + extra space (=0)
int remainingSpace = layoutState.mAvailable + layoutState.mExtraFillSpace;
// Continue to fill more entries when the remaining space is greater than 0
while ((layoutState.mInfinite || remainingSpace > 0) && layoutState.hasMore(state)) {
...
// Populate a single entry
layoutChunk(recycler, state, layoutState, layoutChunkResult)
...
// Subtract the occupied pixel value of the new entry from the remaining spacelayoutState.mAvailable -= layoutChunkResult.mConsumed; remainingSpace -= layoutChunkResult.mConsumed; . }}}Copy the code
Thus it can be concluded that:
RecyclerView Before scrolling occurs, there is an action to fill a new entry, which is not currently displayed.
The RecyclerView fill table is implemented through the while loop, which ends when there is no space left in the list.
How many new entries do I need to populate? Take a look at the exit conditions for the while loop:
public class LinearLayoutManager {
// Fill the entry according to the remaining space
int fill(RecyclerView.Recycler recycler, LayoutState layoutState,RecyclerView.State state, boolean stopOnFocusable) {...// Calculate free space = free space + extra space (=0)
int remainingSpace = layoutState.mAvailable + layoutState.mExtraFillSpace;
// Continue to fill more entries when the remaining space is greater than 0
while ((layoutState.mInfinite || remainingSpace > 0) && layoutState.hasMore(state)) {... }}}Copy the code
Fill the table item number depends on the size of the remainingSpace, its value has two variables together, which layoutState. MExtraFillSpace a value of 0 (breakpoint debugging told me), While layoutstate. mAvailable is determined by the layoutState parameter passed in, search along the call chain for 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);
// Update LayoutState (pass in the absolute displacement value)
updateLayoutState(layoutDirection, absDelta, true, state);
// Fill in the 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 the code
Before the entry is populated, the value of mlayoutstate. mAvailable is set to the absolute value of the roll displacement.
Now we can further refine the previous conclusion:
RecyclerView determines how many new entries need to be filled into the list based on the scrolling displacement before scrolling occurs.
Recycling list items
As new entries are added to the list, old entries are reclaimed, just as new entries move onto the screen and old entries move off the screen as you scroll.
So how do you decide which entries to recycle?
RecycleView recycle.recycleView () recycleView() recycleView() recycleView() recycleView() recycleView() recycleView() recycleView() recycleView() recycleView
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);
/ / 1recycler.recycleView(view); }}}public class LinearLayoutManager {
private void recycleChildren(RecyclerView.Recycler recycler, int startIndex, int endIndex) {
// 2: reclaims the entries whose indexes are endIndex-1 to startIndex
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) {...// Cycle the entry
while ((layoutState.mInfinite || remainingSpace > 0) && layoutState.hasMore(state)) {
// Populate a single entrylayoutChunk(recycler, state, layoutState, layoutChunkResult); .if(layoutState.mScrollingOffset ! = LayoutState.SCROLLING_OFFSET_NaN) { layoutState.mScrollingOffset += layoutChunkResult.mConsumed;// 5: Reclaim the entryrecycleByLayoutState(recycler, layoutState); }... }}}Copy the code
Looking up the call chain (0-5 in the comments), you find an operation to reclaim the entry in the fill() method that fills the entry. This is done immediately after each cycle fills a new entry.
Which entries are recycled?
To answer this question, the logic surrounding recycleChildren(recycler, 0, I) in that code is key:
public class LinearLayoutManager {
private void recycleViewsFromStart(RecyclerView.Recycler recycler, int scrollingOffset,int noRecycleSpace) {...// Iterate over all current entries in the list
for (int i = 0; i < childCount; i++) {
View child = getChildAt(i);
// If the subentry meets a condition, entries with indexes from 0 to i-1 are reclaimed
if (mOrientationHelper.getDecoratedEnd(child) > limit|| mOrientationHelper.getTransformedEndWithDecoration(child) > limit) {
recycleChildren(recycler, 0, i); }}}}Copy the code
Recycling mOrientationHelper is the key to judge condition. The table item getDecoratedEnd (child) > limit.
The mOrientationHelper. GetDecoratedEnd (child) code is as follows:
// Mask the direction of the abstract interface, used to reduce the direction if-else
public abstract class OrientationHelper {
// Gets the distance of the current entry from the top of the list
public abstract int getDecoratedEnd(View view);
// A vertical layout implementation of this interface
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();
returnmLayoutManager.getDecoratedBottom(view) + params.bottomMargin; }}Copy the code
MOrientationHelper. GetDecoratedEnd (child) said that the current table relative to the top of the list at the bottom of the distance, OrientationHelper this abstraction layer shielding the direction of the list, So this sentence can be translated as “the vertical coordinate of the bottom of the current entry relative to the top of the list” in vertical lists.
Judge conditions mOrientationHelper. GetDecoratedEnd (child) > limit of limit is what mean?
In a vertical list, “bottom ordinate of an entry > some value” means that an entry is below a line, meaning that limit is an invisible line in the list, and all entries above that line should be reclaimed.
How is the limit invisible line 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 intlimit = scrollingOffset - noRecycleSpace; .for (int i = 0; i < childCount; i++) {
View child = getChildAt(i);
// If the subentry meets a condition, the entries with indexes from 0 to I are reclaimed
if (mOrientationHelper.getDecoratedEnd(child) > limit|| mOrientationHelper.getTransformedEndWithDecoration(child) > limit) {
recycleChildren(recycler, 0, i); }}}}Copy the code
Limit value is determined by two variables, including noRecycleSpace a value of 0 (this is breakpoint told me, the detailed process can walk RecyclerView animation principle | change the posture to see the source code (pre – layout))
The value of scrollingOffset is passed in externally:
public class LinearLayoutManager {
private void recycleByLayoutState(RecyclerView.Recycler recycler, LayoutState layoutState) {
intscrollingOffset = layoutState.mScrollingOffset; . recycleViewsFromStart(recycler, scrollingOffset, noRecycleSpace); }}Copy the code
Limit value is layoutState. MScrollingOffset’s value, the problem can be converted into layoutState. MScrollingOffset value is decided by what? Where it is assigned in global search:
public class LinearLayoutManager {
private void updateLayoutState(int layoutDirection, int requiredSpace,boolean canUseExistingSpace, RecyclerView.State state) {...int scrollingOffset;
// Get the entry view at the end of the list
final View child = getChildClosestToEnd();
// Calculate the maximum number of pixels the list can scroll without filling it with new entriesscrollingOffset = mOrientationHelper.getDecoratedEnd(child) - mOrientationHelper.getEndAfterPadding(); ./ / mLayoutState. MScrollingOffset the assigned value
mLayoutState.mScrollingOffset = scrollingOffset;
}
int scrollBy(int delta, RecyclerView.Recycler recycler, RecyclerView.State state) {...// Absolute sliding displacement
final int absDelta = Math.abs(delta);
/ / update the LayoutState
updateLayoutState(layoutDirection, absDelta, true, state);
// Before sliding, fill the new entry and reclaim the old entry
final int consumed = mLayoutState.mScrollingOffset + fill(recycler, mLayoutState, state, false); . }}Copy the code
Before sliding occurs and prepare to populate the new table before, call the updateLayoutState (), the method to get the first item list at the end of the table view, and through mOrientationHelper. GetDecoratedEnd (child) calculated the distance between the table at the bottom to the top of the list item, Then subtract the list length. This difference can be interpreted as the maximum number of pixels the list can scroll without filling it with new entries. Slightly abstract, as shown below:
In the figure, the blue border represents the list and the gray rectangle represents the entry.
LayoutManager only loads visible entries. In the figure, half of entry 6 is on the screen, so it is loaded into the list. Entry 7, which is completely invisible, is not loaded. This case, if not continue to fill the table items in a list of 7, the list of most sliding distance is half the length of the table item 6, that is in the code is mLayoutState mScrollingOffset values.
Assume that there is no more data after entry 6, that is, the list slides to the bottom of entry 6. The limit value in this scenario is equal to half the length of entry 6. The limit invisible line should be in the following position:
Take a look at the code for retrieving the entry:
public class LinearLayoutManager {
private void recycleViewsFromStart(RecyclerView.Recycler recycler, int scrollingOffset,int noRecycleSpace) {
final int limit = scrollingOffset - noRecycleSpace;
// Walk through the LinearLayoutManager from scratch to find the entries that should be recycled
final int childCount = getChildCount();
for (int i = 0; i < childCount; i++) {
View child = getChildAt(i);
// If the lower boundary of the entry > limit invisible line
if (mOrientationHelper.getDecoratedEnd(child) > limit
|| mOrientationHelper.getTransformedEndWithDecoration(child) > limit) {
// Reclaim entries with indexes 0 to i-1
recycleChildren(recycler, 0, i);
return; }}}}Copy the code
The recycling logic iterates through the LinearLayoutManager from the beginning. When iterating to entry 1, it finds that its lower boundary is > limit, so the index range of the reclaimed entry is 0 to 0-1, that is, no entries are reclaimed. (Consider that entry 1 has not been completely removed from the screen).
Thus it can be concluded that:
Before RecyclerView sliding occurs, a limit invisible line will be calculated, which is an important basis for deciding which entries should be recycled. When the reclaim logic is triggered, all current entries are traversed. If the bottom of an entry is below the limit invisible line, all entries above the entry are reclaimed.
To make the hypothetical scenario more general, what happens if there is more data after entry 6 and the slide distance is large?
The method updateLayoutState() that calculates the limit value is called in scrollBy() :
public class LinearLayoutManager {
int scrollBy(int delta, RecyclerView.Recycler recycler, RecyclerView.State state) {...// Pass the absolute value of the roll distance to updateLayoutState()
final int absDelta = Math.abs(delta);
updateLayoutState(layoutDirection, absDelta, true, state); . }private void updateLayoutState(int layoutDirection, int requiredSpace,boolean canUseExistingSpace, RecyclerView.State state) {...// Calculate the maximum number of pixels the list can scroll without filling it with new entriesscrollingOffset = mOrientationHelper.getDecoratedEnd(child)- mOrientationHelper.getEndAfterPadding(); .// Store the extra space needed for scrolling in mlayoutstate.mavailablemLayoutState.mAvailable = requiredSpace; mLayoutState.mScrollingOffset = scrollingOffset; . }}Copy the code
Two important values are, in turn, are stored in mLayoutState. MScrollingOffset and mLayoutState mAvailable, respectively is “not populate the new table into the list item, list up to roll how many pixels”, and “is expected to scroll the pixel values”. The former is the basis for how many old entries are reclaimed, and the latter is the basis for how many new entries are populated.
SrollBy () fills the entry immediately after calling updateLayoutState() to store the two important values:
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 entry
final int consumed = mLayoutState.mScrollingOffset + fill(recycler, mLayoutState, state, false); . }}Copy the code
MLayoutState, which stores two important values, is passed as a parameter to fill() :
public class LinearLayoutManager {
int fill(RecyclerView.Recycler recycler, LayoutState layoutState, RecyclerView.State state, boolean stopOnFocusable) {...// Use the expected slip distance as the basis for how many new entries to fill
int remainingSpace = layoutState.mAvailable + layoutState.mExtraFillSpace;
while ((layoutState.mInfinite || remainingSpace > 0) && layoutState.hasMore(state)) {
// Populate a single entrylayoutChunk(recycler, state, layoutState, layoutChunkResult); .. / / in layoutState mScrollingOffset on the consumption of additional item for the new table fill pixels
layoutState.mScrollingOffset += layoutChunkResult.mConsumed;
// Reclaim the entryrecycleByLayoutState(recycler, layoutState); . }... }}Copy the code
In circular populate the new table items, new table entries occupy the pixel values always appended to the layoutState. MScrollingOffset, namely its value in increasing (limit contact line is down). At the end of a while loop, recycleByLayoutState() is called to recycle entries based on the current limit invisible line position:
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();
// Iterate over the entry
for (int i = 0; i < childCount; i++) {
View child = getChildAt(i);
When the bottom of an entry is behind the limit invisible line, reclaim all entries above it
if (mOrientationHelper.getDecoratedStart(child) > limit || mOrientationHelper.getTransformedStartWithDecoration(child) > limit) {
recycleChildren(recycler, 0, i);
return; }}}}Copy the code
Each time you populate the end of the list with an entry, the position of the limit invisible line moves the number of pixels occupied by the entry down, so that more entries in the head of the list are eligible for collection.
The analysis of the details about recycling, can walk RecyclerView caching mechanism | recycling where? .
Sum it up with a pictureLimit contact line(Red dotted line in figure) :
The value of limit represents the total distance of the actual scroll at this time. (This is an ideal situation where the bottom of the newly inserted entry 7 overlaps the bottom of the list after scrolling)
The limit invisible line can be understood as: the current position of the invisible line will overlap with the top of the list after scrolling
conclusion
RecyclerView determines how many new entries need to be filled into the list based on the expected scrolling displacement before scrolling occurs.
RecyclerView fills entries one by one through the while loop, which ends when the list runs out of space.
Before RecyclerView sliding occurs, a limit invisible line will be calculated, which is an important basis for deciding which entries should be recycled. It can be interpreted as: the current position of the invisible line will overlap with the top of the list after scrolling
Limit The initial value of the invisible line is the distance from the bottom of the visible entry to the bottom of the list. That is, the maximum distance that the list can slide without filling new entries. The pixel value consumed by each newly populated entry is appended to the limit value, which means that the invisible line of the limit is continuously moved down as new entries are populated.
When the reclaim logic is triggered, all current entries are traversed. If the bottom of an entry is below the limit invisible line, all entries above the entry are reclaimed.
Recommended reading
RecyclerView series article directory is as follows:
-
RecyclerView caching mechanism | how to reuse table?
-
What RecyclerView caching mechanism | recycling?
-
RecyclerView caching mechanism | recycling where?
-
RecyclerView caching mechanism | scrap the view of life cycle
-
Read the source code long knowledge better RecyclerView | click listener
-
Proxy mode application | every time for the new type RecyclerView is crazy
-
Better RecyclerView table sub control click listener
-
More efficient refresh RecyclerView | DiffUtil secondary packaging
-
Change an idea, super simple RecyclerView preloading
-
RecyclerView animation principle | change the posture to see the source code (pre – layout)
-
RecyclerView animation principle | pre – layout, post – the relationship between the layout and scrap the cache
-
RecyclerView animation principle | how to store and use animation attribute values?
-
RecyclerView list of interview questions | scroll, how the list items are filled or recycled?
-
RecyclerView interview question | what item in the table below is recycled to the cache pool?
-
RecyclerView performance optimization | to halve load time table item (a)
-
RecyclerView performance optimization | to halve load time table item (2)
-
RecyclerView performance optimization | to halve load time table item (3)
-
How does RecyclerView roll? (a) | unlock reading source new posture
-
RecyclerView how to achieve the scrolling? (2) | Fling
-
RecyclerView Refresh list data notifyDataSetChanged() why is it expensive?