RecyclerView的源码还是比较庞大的,它提供了使用方便的功能,这背后需要优秀的设计支撑。比如它一个看似简单的View布局流程,牵涉到的点却比想象中的多。
RecyclerView的布局流程
RecyclerView是个ViewGroup,它的布局过程并没有在自己内部去做,而是交给了LayoutManager。LayoutManager所需要布局的View是由Adapter提供的,但它并没有直接去从Adapter中获取,而是通过Recycler。
看完整体设计图后,可以顺便思考下这个问题:
为什么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的功能方法就好了。