最近在研究设计模式的时候看到了Adapter模式,第一时间就想到了RecyclerView用到的Adapter,简单地走了一遍ReyclerView相关的源码,不得不感叹:设计得真的漂亮。
本文算不上源码分析,只能算是理解设计模式的初级内容。
平时使用RecyclerView的时候大只可分为三个部分:
1.Adapter
2.LayoutManager
3.RcyclerView
将这三个部分组合在一起就构成了一个漂亮的「多视图展示」的View,作为这么一个优秀的控件,整体的显示结构可以用下图表示:
从右往左看,假设对与不同类型的Layout以及不同格式的数据,通过一个Adapter适配成为一个个ViewHolder,ViewHolder中缓存有每个用于显示的ItemView。ItemView的布局操作则交给了LanyoutManager来管理,ItemView可以根据LayoutManager中的布局策略,完成自己的布局操作,如果不想用系统提供的那三种LayoutManager,完全可以自己根据需求来定制一个,通过Adapter和LayoutManger,整个RecyclerView的功能变得十分强大,可定制性超级高。
说到底,RcyclerView终究只是一个ViewGroup,就从它的的onMeasure方法开始简单跟进一下,捋一捋LayoutManager和Adapter的使用时机,这样在以后的定制过程中会有更深的理解。
定位到onMeasure方法,先把主线拎出来,如下图:
从整个方法的调用链可以看出:在onMeasure开始执行的时候,就将measure操作委托给了LayoutManager。
@Override
protected void onMeasure(int widthSpec, int heightSpec) {
if (mLayout == null) {
// 如果没有设置LayoutManager,执行默认的测量操作
defaultOnMeasure(widthSpec, heightSpec);
return;
}
//如果设置了LayoutManager,先判断是否开启了自动测量
if (mLayout.mAutoMeasure) {
//开启了自动测量,根据ItemView所占大小,设置RecyclerView
//将测量的操作委托给LayoutManager(mLayout就是LayoutManager的实例)
mLayout.onMeasure(mRecycler, mState, widthSpec, heightSpec);
……
} else {
//没有开启自动测量
//如果设置了固定的大小则直接将测量的操作委托给LayoutManager
if (mHasFixedSize) {
mLayout.onMeasure(mRecycler, mState, widthSpec, heightSpec);
return;
}
//如果没有设置固定大小,执行自定义的测量流程
……
}
}
系统提供的几种layoutManager都有开启自动测量的功能,即LayoutManager会更具ItemView的大小,自动测量RecyclerView的宽高,具体算法我就没去深入了。
测绘完了后,LayoutManger就会给ItemView进行布局操作了,跳到onLayoutChildren方法中,这个方法的代码比较多,有200行左右,开头的注释描述了具体的布局算法,忽略掉相关的判断操作,最终都调用了一个fill()方法来实现来填充ItemView。
@Override
public void onLayoutChildren(RecyclerView.Recycler recycler, RecyclerView.State state) {
// layout algorithm:
// 1) by checking children and other variables, find an anchor coordinate and an anchor
// item position.
// 2) fill towards start, stacking from bottom
// 3) fill towards end, stacking from top
// 4) scroll to fulfill requirements like stack from bottom.
····
if (mAnchorInfo.mLayoutFromEnd) {
// fill towards start
···
fill(recycler, mLayoutState, state, false);
····
// fill towards end
····
fill(recycler, mLayoutState, state, false);
····
} else {
····
}
····
}
由于我真是抱着学习RecyclerView工作大致流程的心态去分析,也就没深入各种逻辑细节了,直接看到fill()方法。
int fill(RecyclerView.Recycler recycler, LayoutState layoutState,
RecyclerView.State state, boolean stopOnFocusable) {
···
while ((layoutState.mInfinite || remainingSpace > 0) && layoutState.hasMore(state)) {
···
//迭代布局ItemView
layoutChunk(recycler, state, layoutState, layoutChunkResult);
···
}
···
}
看到循环了,顿时就松了一口气,大概也知道具体布局的ItemView的工作多半都是在循环中迭代完成的。
void layoutChunk(RecyclerView.Recycler recycler, RecyclerView.State state,
LayoutState layoutState, LayoutChunkResult result) {
View view = layoutState.next(recycler);
···
// We calculate everything with View's bounding box (which includes decor and margins)
// To calculate correct layout position, we subtract margins.
layoutDecoratedWithMargins(view, left, top, right, bottom);
···
}
走到这一步了,注释也说得过去很明白了,计算得到的数据最终会传入layoutDecoratedWithMargins()方法来完成布局,再点进这个方法:
public void layoutDecoratedWithMargins(View child, int left, int top, int right,
int bottom) {
final LayoutParams lp = (LayoutParams) child.getLayoutParams();
final Rect insets = lp.mDecorInsets;
child.layout(left + insets.left + lp.leftMargin, top + insets.top + lp.topMargin,
right - insets.right - lp.rightMargin,
bottom - insets.bottom - lp.bottomMargin);
}
终于看到了朝思暮想的layout方法,也走到了ItemView布局的终点。
到此为止,LayoutManager的主线已经被拎出来了,下面就回退到layoutChunk() 中,不可以忽略第一句话View view = layoutState.next(recycler);
, 这里获取View的方法尤为重要,应为View的来源是ViewHolder,而Adapter又管理者ViewHolder,自然而然Adapter的调用时机就在这里:
View next(RecyclerView.Recycler recycler) {
if (mScrapList != null) {
return nextViewFromScrapList();
}
final View view = recycler.getViewForPosition(mCurrentPosition);
mCurrentPosition += mItemDirection;
return view;
}
走到这,recycler出现了,他是Recycler的实例,也是负责复用与管理ItemView的类。在这个类里面可以轻松找到三个缓存ViewHolder的集合:
final ArrayList<ViewHolder> mAttachedScrap = new ArrayList<>();
ArrayList<ViewHolder> mChangedScrap = null;
final ArrayList<ViewHolder> mCachedViews = new ArrayList<ViewHolder>();
具体的复用缓存机制就不做深入了,点进getViewForPosition():
View getViewForPosition(int position, boolean dryRun) {
return tryGetViewHolderForPositionByDeadline(position, dryRun, FOREVER_NS).itemView;
}
发现调用的其实是:tryGetViewHolderForPositionByDeadline():
ViewHolder tryGetViewHolderForPositionByDeadline(int position,
boolean dryRun, long deadlineNs) {
···
holder = mAdapter.createViewHolder(RecyclerView.this, type);
···
return holder;
}
终于看到了熟悉的createViewHolder()方法,这不正是我们实现Adapter时重写的几个方法之一吗?走到这里,Adapter的使用时机大概也知道了,也就完成了分析RecyclerView的目的,就不往下继续走了。很显然ViewHolder的构建需要上层使用者去具体完成,在RecyclerView#Adapter中定义的仅仅是一个抽象的Adapter。
最后总结一下,从设计模式的角度来将,ReyclerView的设计确实十分漂亮,LayoutManager和Adapter各司其职,协同合作,共同实现ReyclerView的功能,并且下层抽象的ViewHodler和Adapter也定义了相关的实现规范,使得上层用户在使用的时候学习成本非常低,并且无需关注优化的细节,不得不说“适配器模式”在RecyclerView的设计中运用的十分合适。
参考资料:RecyclerView源码分析(二)–测量流程