RecyclerView的布局流程设计

1,717 阅读2分钟

RecyclerView的源码还是比较庞大的,它提供了使用方便的功能,这背后需要优秀的设计支撑。比如它一个看似简单的View布局流程,牵涉到的点却比想象中的多。

RecyclerView的布局流程

RecyclerView是个ViewGroup,它的布局过程并没有在自己内部去做,而是交给了LayoutManagerLayoutManager所需要布局的View是由Adapter提供的,但它并没有直接去从Adapter中获取,而是通过Recycler

recyclerview的布局架构.png

看完整体设计图后,可以顺便思考下这个问题:

为什么LayoutManager不直接通过Adapter去获取View呢? (文末给出笔者的思考)

只看森林不寻木

下面只分析下有关RecyclerView布局整体设计的核心源码。为了剔除细节干扰,删除了大量无关代码。

LayoutManager

RecyclerView的测量和布局任务都会交给LayoutManager处理

# RecyclerView
LayoutManager mLayout
// 测量
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);
}

LayoutManager的测量操作:

# 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);
}

LayoutManager的布局操作,必须要实现类重写,因为不同的布局类型,会有不同的布局方式

public abstract static class LayoutManager {
  // 具体实现类重写
  public void onLayoutChildren(Recycler recycler, State state) {
    Log.e(TAG, "You must override onLayoutChildren(Recycler recycler, State state) ");
  }
}       

比如具体实现LinearLayoutManager

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);
 }
}

LayoutManager获取View的方式:它并没有直接向RecyclerView,而是通过Recycler获取。以布局过程填充view为例:

// 填充View的过程
int fill(RecyclerView.Recycler recycler, LayoutState layoutState,
            RecyclerView.State state, 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;
}

Recycler

Recycler的创建在Recyclerview类中创建

final Recycler mRecycler = new Recycler();

Recycler负责回收管理,并对外提供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;
  // 根据缓存获取VH
  ...
  // 如果没有缓存可用,通过Adapter去创建
  if (holder == null) {
    holder = mAdapter.createViewHolder(RecyclerView.this, type);
    ...
    return holder;
  }
}

Adapter

Recyclerview抽出了一个抽象类Adapter,从泛型类型<VH extends ViewHolder>可以看出,它强行与ViewHolder建立了关联。它提供了创建ViewHolder,和绑定数据到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());
  }
}

回答问题

为什么LayoutManager不直接通过Adapter去获取View呢?

LayoutManager是借助Recycler这个类来获取View的,因为Recycler的职责关键是缓存复用ViewHolder,所以就让它来掌管整个ViewHolder的创建和复用。这样对于需要使用View的来说,只要通过Recycler就可以获取到想要的View

从设计角度来看,这几个类职责规划到很清晰,对于LayoutManager来说就并不需要关心ViewHolder是如何通过Adapter创建View的,它只需要关心Recycler提供获取View的功能方法就好了。