RecyclerView source code is relatively large, it provides the use of convenient functions, which need excellent design support behind. For example, a seemingly simple View layout process involves more points than expected.

RecyclerView layout process

RecyclerView is a ViewGroup, its layout process is not done in its own internal, but handed over to the LayoutManager. The View that LayoutManager needs to recycle is provided by the Adapter, but it doesn’t go to the Adapter directly, it goes through Recycler.

After looking at the overall design drawing, you can think about this question:

Why doesn’t LayoutManager just fetch the View through the Adapter? (The author’s thinking is given at the end of the article)

Look for the forest and not the wood

The following analysis of RecyclerView layout overall design of the core source code. A lot of extraneous code was removed in order to weed out details.

LayoutManager

The measurement and layout of RecyclerView are all handled by LayoutManager

# RecyclerView LayoutManager // measure protected void onMeasure(int widthSpec, int heightSpec) { mLayout.onMeasure(mRecycler, mState, widthSpec, heightSpec); mLayout.setMeasuredDimensionFromChildren(widthSpec, heightSpec); } // protected void onLayout(Boolean changed, int l, int t, int r, int b) {dispatchLayout(); mFirstLayoutComplete = true; } void dispatchLayout() { dispatchLayoutStep1(); mLayout.setExactMeasureSpecsFrom(this); dispatchLayoutStep2(); } private void dispatchLayoutStep1() { mLayout.onLayoutChildren(mRecycler, mState); }Copy the code

LayoutManager measurement operations:

# LayoutManager public void onMeasure(Recycler recycler, State state, int widthSpec, int heightSpec) { mRecyclerView.defaultOnMeasure(widthSpec, heightSpec); } void defaultOnMeasure(int widthSpec, int heightSpec) { final int width = LayoutManager.chooseSize(widthSpec, getPaddingLeft() + getPaddingRight(), getMinimumWidth()); final int height = LayoutManager.chooseSize(heightSpec, getPaddingTop() + getPaddingBottom(), getMinimumHeight()); setMeasuredDimension(width, height); } void setMeasuredDimensionFromChildren(int widthSpec, int heightSpec) { final int count = getChildCount(); for (int i = 0; i < count; i++) { View child = getChildAt(i); . } setMeasuredDimension(mRecyclerView.mTempRect, widthSpec, heightSpec); }Copy the code

LayoutManager layout operations must implement class rewriting, because different layout types can have different layouts

Public void onLayoutChildren(Recycler Recycler, State state) { Log.e(TAG, "You must override onLayoutChildren(Recycler recycler, State state) "); }}Copy the code

For example, implementing the LinearLayoutManager specifically:

public class LinearLayoutManager extends RecyclerView.LayoutManager implements ItemTouchHelper.ViewDropHandler, RecyclerView.SmoothScroller.ScrollVectorProvider{ @Override public void onLayoutChildren(RecyclerView.Recycler recycler,  RecyclerView.State state) { // calculate anchor position and coordinate updateAnchorInfoForLayout(recycler, state, mAnchorInfo); calculateExtraLayoutSpace(state, mReusableIntPair); detachAndScrapAttachedViews(recycler); . fill(recycler, mLayoutState, state, false); }}Copy the code

The way LayoutManager gets Views: It doesn’t get RecyclerView directly, it gets views through Recycler. Take the layout process filling a View as an example:

Int fill(RecyclerView.Recycler Recycler, LayoutState LayoutState, RecyclerView.State State, RecyclerView. boolean stopOnFocusable) { ... layoutChunk(recycler, state, layoutState, layoutChunkResult); } void layoutChunk(RecyclerView.Recycler recycler, RecyclerView.State state, LayoutState layoutState, LayoutChunkResult result) { View view = layoutState.next(recycler); if (mShouldReverseLayout == (layoutState.mLayoutDirection == LayoutState.LAYOUT_START)) { addView(view); } else { addView(view, 0); } } View next(RecyclerView.Recycler recycler) { final View view = recycler.getViewForPosition(mCurrentPosition); mCurrentPosition += mItemDirection; return view; }Copy the code

Recycler

Recycler is created in Recyclerview

final Recycler mRecycler = new Recycler();
Copy the code

Recycler is responsible for recycling management and provides view

View getViewForPosition(int position, boolean dryRun) { // viewHolder.itemView return tryGetViewHolderForPositionByDeadline(position, dryRun, FOREVER_NS).itemView; } ViewHolder tryGetViewHolderForPositionByDeadline(int position, boolean dryRun, long deadlineNs) { ViewHolder holder = null; // Get VH from cache... / / if there is no cache is available through the Adapter to create the if (holder = = null) {holder = mAdapter. CreateViewHolder (RecyclerView. This type); . return holder; }}Copy the code

Adapter

Recyclerview extracts an abstract class Adapter, which, as you can see from the generic type

, forces an association with the ViewHolder. It provides the ability to create a ViewHolder and bind data to the ViewHolder.

public abstract static class Adapter<VH extends ViewHolder> { private final AdapterDataObservable mObservable = new AdapterDataObservable(); public final VH createViewHolder(ViewGroup parent, int viewType) { ... VH holder = onCreateViewHolder(parent, viewType); return holder; } public final void bindViewHolder(VH holder, int position) { ... onBindViewHolder(holder, position, holder.getUnmodifiedPayloads()); }}Copy the code

Answer the questions

whyLayoutManagerNot directly throughAdapterTo obtainView?

LayoutManager uses the Recycler class to retrieve views. Because Recycler is responsible for caching ViewHolder reuse, LayoutManager is responsible for creating and recycling viewholders. So people who need to use Recycler can use Recycler.

From a design point of View, these responsibilities are clearly defined. LayoutManager does not need to be concerned about how the ViewHolder creates views through the Adapter. LayoutManager only needs to be concerned about the Recycler providing functional methods to obtain views.