#### **[RecyclerView](https://www.androidos.net.cn/android/5.0.1_r1/xref/frameworks/support/v7/recyclerview/src/android/support/v7/widget/RecyclerView.java) 设计与实现(源码解读RecyclerView )**
RecyclerView有很多内部类和接口,具体如下图所示:
:-: ![](https://box.kancloud.cn/0dc9cd0a0605e772a586e596b5599436_362x438.jpg)
RecyclerView的内部类
对于RecyclerView 和ListView 来说,比较**相同的一点是使用了Adapter 和观察者模式**,相关的代码如下。
**RecyclerView.java**
~~~
package android.support.v7.widget;
public class RecyclerView extends ViewGroup {
private static final String TAG = "RecyclerView";
..............
public void setAdapter(Adapter adapter) {
setAdapterInternal(adapter, false, true);
requestLayout();
}
private void setAdapterInternal(Adapter adapter, boolean compatibleWithPrevious,
boolean removeAndRecycleViews) {
if (mAdapter != null) {
mAdapter.unregisterAdapterDataObserver(mObserver);
}
if (!compatibleWithPrevious || removeAndRecycleViews) {
if (mItemAnimator != null) {
mItemAnimator.endAnimations();
}
if (mLayout != null) {
mLayout.removeAndRecycleAllViews(mRecycler);
mLayout.removeAndRecycleScrapInt(mRecycler, true);
}
}
mAdapterHelper.reset();
final Adapter oldAdapter = mAdapter;
mAdapter = adapter;
if (adapter != null) {
//注册观察者
adapter.registerAdapterDataObserver(mObserver);
}
if (mLayout != null) {
mLayout.onAdapterChanged(oldAdapter, mAdapter);
}
mRecycler.onAdapterChanged(oldAdapter, mAdapter, compatibleWithPrevious);
//设置结构发生改变的标志位
mState.mStructureChanged = true;
// 刷新视图
markKnownViewsInvalid();
}
............
}
~~~
在**用setAdapter时最终也会注册一个观察者**,这个观察者具体实现类是RecyclerView 的内部类**RecyclerViewDataObserver**, 具体代码如下。
**RecyclerView.RecyclerViewDataObserver**
~~~
private class RecyclerViewDataObserver extends AdapterDataObserver {
@Override
public void onChanged() {
assertNotInLayoutOrScroll(null);
if (mAdapter.hasStableIds()) {
mState.mStructureChanged = true;
mDataSetHasChangedAfterLayout = true;
} else {
mState.mStructureChanged = true;
mDataSetHasChangedAfterLayout = true;
}
//需要重新布局
if (!mAdapterHelper.hasPendingUpdates()) {
requestLayout();
}
}
@Override
public void onItemRangeChanged(int positionStart, int itemCount) {
assertNotInLayoutOrScroll(null);
if (mAdapterHelper.onItemRangeChanged(positionStart, itemCount)) {
triggerUpdateProcessor();
}
}
@Override
public void onItemRangeInserted(int positionStart, int itemCount) {
assertNotInLayoutOrScroll(null);
if (mAdapterHelper.onItemRangeInserted(positionStart, itemCount)) {
triggerUpdateProcessor();
}
}
@Override
public void onItemRangeRemoved(int positionStart, int itemCount) {
assertNotInLayoutOrScroll(null);
if (mAdapterHelper.onItemRangeRemoved(positionStart, itemCount)) {
triggerUpdateProcessor();
}
}
@Override
public void onItemRangeMoved(int fromPosition, int toPosition, int itemCount) {
assertNotInLayoutOrScroll(null);
if (mAdapterHelper.onItemRangeMoved(fromPosition, toPosition, itemCount)) {
triggerUpdateProcessor();
}
}
void triggerUpdateProcessor() {
if (mPostUpdatesOnAnimation && mHasFixedSize && mIsAttached) {
ViewCompat.postOnAnimation(RecyclerView.this, mUpdateChildViewsRunnable);
} else {
mAdapterUpdateDuringMeasure = true;
requestLayout();
}
}
}
~~~
**在数据集发生变化且调用了Adapter 的notifyDataSetChanged之后就会调用RecyclerViewDataObserver 的onChanged 函数,在该函数中又会调用RecyclerView 的requestLayout函数进行重新布局**。这些过程与ListView 的实现基本一致,最大的不同在于它们之间的布局实现上。**在ListView中的布局是通过自身的layoutChilden 函数来实现,而对于RecyclerView 来说它的布局职责则是交给了LayoutManager 对象**。
**RecyclerView:setLayoutManager()**
~~~
package android.support.v7.widget;
public class RecyclerView extends ViewGroup {
private static final String TAG = "RecyclerView";
..............
private Adapter mAdapter;
private LayoutManager mLayout;
............
public void setLayoutManager(LayoutManager layout) {
if (layout == mLayout) {
return;
}
if (mLayout != null) {
if (mIsAttached) {
mLayout.onDetachedFromWindow(this, mRecycler);
}
mLayout.setRecyclerView(null);
}
mRecycler.clear();
mChildHelper.removeAllViewsUnfiltered();
mLayout = layout;
if (layout != null) {
if (layout.mRecyclerView != null) {
throw new IllegalArgumentException("LayoutManager " + layout +
" is already attached to a RecyclerView: " + layout.mRecyclerView);
}
mLayout.setRecyclerView(this);
if (mIsAttached) {
mLayout.onAttachedToWindow(this);
}
}
//设置布局管理器之后重新布局
requestLayout();
}
............
@Override
public void requestLayout() {
if (!mEatRequestLayout) {
super.requestLayout();
} else {
mLayoutRequestEaten = true;
}
}
..................
}
~~~
**在设置了布局管理器之后就会调用requestLayout 函数进行布局,然后会调用onLayout 函数**
**RecyclerView:onLayout()、dispatchLayout()**
~~~
package android.support.v7.widget;
public class RecyclerView extends ViewGroup {
private static final String TAG = "RecyclerView";
..............
void dispatchLayout() {
.............
//获取工tern 数量
mState.mItemCount = mAdapter.getItemCount();
mState.mDeletedInvisibleItemCountSincePreviousLayout = 0;
// Step 2: Run layout
mState.mInPreLayout = false;
//执行布局,调用LayoutManager 的onLayoutChilden函数
mLayout.onLayoutChildren(mRecycler, mState);
mState.mStructureChanged = false;
mPendingSavedState = null;
mState.mRunSimpleAnimations = mState.mRunSimpleAnimations && mItemAnimator != null;
// 含有动画则执行item 动画
if (mState.mRunSimpleAnimations) {
.................
}
................
}
............
@Override
protected void onLayout(boolean changed, int l, int t, int r, int b) {
eatRequestLayout();
//分发layout
dispatchLayout();
resumeRequestLayout(false);
mFirstLayoutComplete = true;
}
..............
}
~~~
**在onLayout 函数中最终会调用dispatchLayout 函数,而在dispatchLayout 函数中又会调用LayoutManager 的onLayoutChilden 函数进行布局**。在此,我们以LinearLayoutManager为例进行学习。下面看看[LinearLayoutManager](https://www.androidos.net.cn/android/5.0.1_r1/xref/frameworks/support/v7/recyclerview/src/android/support/v7/widget/LinearLayoutManager.java) 中的onLayoutChilden 函数。
**LinearLayoutManager:onLayoutChildren()、fill()**
~~~
package android.support.v7.widget;
public class LinearLayoutManager extends RecyclerView.LayoutManager {
private static final String TAG = "LinearLayoutManager";
...............
/**
* {@inheritDoc}
*/
@Override
public void onLayoutChildren(RecyclerView.Recycler recycler, RecyclerView.State state) {
..............
int startOffset;
int endOffset;
onAnchorReady(state, mAnchorInfo);
detachAndScrapAttachedViews(recycler);
mLayoutState.mIsPreLayout = state.isPreLayout();
if (mAnchorInfo.mLayoutFromEnd) {
.............
} else {
// 从上到下布局
updateLayoutStateToFillEnd(mAnchorInfo);
mLayoutState.mExtra = extraForEnd;
fill(recycler, mLayoutState, state, false);
endOffset = mLayoutState.mOffset;
if (mLayoutState.mAvailable > 0) {
extraForStart += mLayoutState.mAvailable;
}
// fill towards start
updateLayoutStateToFillStart(mAnchorInfo);
mLayoutState.mExtra = extraForStart;
mLayoutState.mCurrentPosition += mLayoutState.mItemDirection;
//填充Item View
fill(recycler, mLayoutState, state, false);
startOffset = mLayoutState.mOffset;
}
..............
}
..............
int fill(RecyclerView.Recycler recycler, LayoutState layoutState,
RecyclerView.State state, boolean stopOnFocusable) {
// 存储当前可用空间
final int start = layoutState.mAvailable;
................
// 1.计算RecyclerView 的可用布局宽或高
int remainingSpace = layoutState.mAvailable + layoutState.mExtra;
LayoutChunkResult layoutChunkResult = new LayoutChunkResult();
// 2.迭代布局item view
while (remainingSpace > 0 && layoutState.hasMore(state)) {
layoutChunkResult.resetInternal();
//3.布局item View
layoutChunk(recycler, state, layoutState, layoutChunkResult);
if (layoutChunkResult.mFinished) {
break;
}
// 4.计算布局偏移量
layoutState.mOffset += layoutChunkResult.mConsumed * layoutState.mLayoutDirection;
if (!layoutChunkResult.mIgnoreConsumed || mLayoutState.mScrapList != null
|| !state.isPreLayout()) {
layoutState.mAvailable -= layoutChunkResult.mConsumed;
// 5.计算剩余的可用空间
remainingSpace -= layoutChunkResult.mConsumed;
}
................
}
if (DEBUG) {
validateChildOrder();
}
return start - layoutState.mAvailable;
}
..............
}
~~~
**在onLayoutChilden 函数中会调用fill函数,在fill 函数中又会循环地调用layoutChunk 函数进行布局,每次布局完之后就会计算当前屏幕剩余的可利用空间,并且做出判断是否还需要布局ItemView**。因此,先看看layoutChunk的实现。
**LinearLayoutManager:layoutChunk()**
~~~
void layoutChunk(RecyclerView.Recycler recycler, RecyclerView.State state,
LayoutState layoutState, LayoutChunkResult result) {
// 1.获取Item View
View view = layoutState.next(recycler);
...............
//2.获取Item View的布局参数
RecyclerView.LayoutParams params = (RecyclerView.LayoutParams) view.getLayoutParams();
// 3.丈量Item View
measureChildWithMargins(view, 0, 0);
// 4.计算该Item View消耗的宽度或高度
result.mConsumed = mOrientationHelper.getDecoratedMeasurement(view);
// Item View的上下左右坐标位置
int left, top, right, bottom;
// 5.按照水平或竖直方向布局,计算Item View 的上下左右坐标
if (mOrientation == VERTICAL) {
if (isLayoutRTL()) {
right = getWidth() - getPaddingRight();
left = right - mOrientationHelper.getDecoratedMeasurementInOther(view);
} else {
left = getPaddingLeft();
right = left + mOrientationHelper.getDecoratedMeasurementInOther(view);
}
if (layoutState.mLayoutDirection == LayoutState.LAYOUT_START) {
bottom = layoutState.mOffset;
top = layoutState.mOffset - result.mConsumed;
} else {
top = layoutState.mOffset;
bottom = layoutState.mOffset + result.mConsumed;
}
} else {
//竖直方向布局的计算方式
}
// 6 . 布局item view
layoutDecorated(view, left + params.leftMargin, top + params.topMargin,
right - params.rightMargin, bottom - params.bottomMargin);
if (DEBUG) {
Log.d(TAG, "laid out child at position " + getPosition(view) + ", with l:"
+ (left + params.leftMargin) + ", t:" + (top + params.topMargin) + ", r:"
+ (right - params.rightMargin) + ", b:" + (bottom - params.bottomMargin));
}
if (params.isItemRemoved() || params.isItemChanged()) {
result.mIgnoreConsumed = true;
}
result.mFocusable = view.isFocusable();
}
~~~
**在layoutChunk 中首先从layoutState 中获取到Item View ,然后获取Item View 的布局参数、尺寸信息,并且根据布局方式(横向或者纵向)计算出Item View的上下左右坐标,最后调用layoutDecorated 函数实现布局**。
layoutDecorated 函数定义在LayoutManager 中,具体代码如下。
**RecyclerView.LayoutManager:layoutDecorated()**
~~~
public void layoutDecorated(View child, int left, int top, int right, int bottom) {
final Rect insets = ((LayoutParams) child.getLayoutParams()).mDecorInsets;
child.layout(left + insets.left, top + insets.top, right - insets.right,
bottom - insets.bottom);
}
~~~
**从上述程序可以看到, 只是调用了Item View 的layout函数( child.layout函数)将Item View 布局到具体的位置。这样一来,就将布局的职责从RecyclerView 分离到LayoutManager 中,也使得RecyclerView 更为灵活。**
这里的**layoutChunk 函数很重要**,**首先通过LayoutState 对象的next 函数获取到Item View**,这里也是一个重要的地方。我们看看LayoutState 函数的next 实现。
**LinearLayoutManager.LayoutState:next()**
~~~
package android.support.v7.widget;
public class LinearLayoutManager extends RecyclerView.LayoutManager {
private static final String TAG = "LinearLayoutManager";
...............
static class LayoutState {
..............
View next(RecyclerView.Recycler recycler) {
if (mScrapList != null) {
return nextFromLimitedList();
}
//调用Recycler中的getViewForPosition获取item View
final View view = recycler.getViewForPosition(mCurrentPosition);
mCurrentPosition += mItemDirection;
return view;
}
.............
}
..............
}
~~~
**实际上就是调用RecyclerView.Recycler 对象getViewForPosition 函数获取到Item View** ,我们继续深RecyclerView.Recycler 类的相关代码。
**RecyclerView.Recycler:getViewForPosition()、getChangedScrapViewForPosition()、getScrapViewForPosition()**
~~~
package android.support.v7.widget;
public class RecyclerView extends ViewGroup {
private static final String TAG = "RecyclerView";
..............
public final class Recycler {
final ArrayList<ViewHolder> mAttachedScrap = new ArrayList<ViewHolder>();
private ArrayList<ViewHolder> mChangedScrap = null;
final ArrayList<ViewHolder> mCachedViews = new ArrayList<ViewHolder>();
................
//根据position 获取该位置对应的View
public View getViewForPosition(int position) {
return getViewForPosition(position, false);
}
View getViewForPosition(int position, boolean dryRun) {
if (position < 0 || position >= mState.getItemCount()) {
throw new IndexOutOfBoundsException("Invalid item position " + position
+ "(" + position + "). Item count:" + mState.getItemCount());
}
boolean fromScrap = false;
ViewHolder holder = null;
// 1.从mChangedScrap中获取ViewHolder缓存
if (mState.isPreLayout()) {
holder = getChangedScrapViewForPosition(position);
fromScrap = holder != null;
}
// 2.从mAttachedScrap中获取ViewHolder缓存
if (holder == null) {
holder = getScrapViewForPosition(position, INVALID_TYPE, dryRun);
..................
}
if (holder == null) {
final int offsetPosition = mAdapterHelper.findPositionOffset(position);
.................
//从其他ViewHolder缓存中检测是否有缓存,代码省略
// 3.没有ViewHolder,则需要创建ViewHolder,这里会调用onCreateViewHolder 函数
if (holder == null) {
holder = mAdapter.createViewHolder(RecyclerView.this,
mAdapter.getItemViewType(offsetPosition));
if (DEBUG) {
Log.d(TAG, "getViewForPosition created new ViewHolder");
}
}
}
boolean bound = false;
if (mState.isPreLayout() && holder.isBound()) {
holder.mPreLayoutPosition = position;
} else if (!holder.isBound() || holder.needsUpdate() || holder.isInvalid()) {
if (DEBUG && holder.isRemoved()) {
throw new IllegalStateException("Removed holder should be bound and it should"
+ " come here only in pre-layout. Holder: " + holder);
}
final int offsetPosition = mAdapterHelper.findPositionOffset(position);
// 4.绑定数据,这里会调用Adapter的onBindViewHolder
mAdapter.bindViewHolder(holder, offsetPosition);
attachAccessibilityDelegate(holder.itemView);
bound = true;
if (mState.isPreLayout()) {
holder.mPreLayoutPosition = position;
}
}
// 设置Item View 的LayoutParams
final ViewGroup.LayoutParams lp = holder.itemView.getLayoutParams();
final LayoutParams rvLayoutParams;
if (lp == null) {
rvLayoutParams = (LayoutParams) generateDefaultLayoutParams();
holder.itemView.setLayoutParams(rvLayoutParams);
} else if (!checkLayoutParams(lp)) {
rvLayoutParams = (LayoutParams) generateLayoutParams(lp);
holder.itemView.setLayoutParams(rvLayoutParams);
} else {
rvLayoutParams = (LayoutParams) lp;
}
rvLayoutParams.mViewHolder = holder;
rvLayoutParams.mPendingInvalidate = fromScrap && bound;
// 5.返回itemView
return holder.itemView;
}
................
ViewHolder getChangedScrapViewForPosition(int position) {
// If pre-layout, check the changed scrap for an exact match.
final int changedScrapSize;
if (mChangedScrap == null || (changedScrapSize = mChangedScrap.size()) == 0) {
return null;
}
// find by position
for (int i = 0; i < changedScrapSize; i++) {
final ViewHolder holder = mChangedScrap.get(i);
if (!holder.wasReturnedFromScrap() && holder.getPosition() == position) {
holder.addFlags(ViewHolder.FLAG_RETURNED_FROM_SCRAP);
return holder;
}
}
// find by id
if (mAdapter.hasStableIds()) {
final int offsetPosition = mAdapterHelper.findPositionOffset(position);
if (offsetPosition > 0 && offsetPosition < mAdapter.getItemCount()) {
final long id = mAdapter.getItemId(offsetPosition);
for (int i = 0; i < changedScrapSize; i++) {
final ViewHolder holder = mChangedScrap.get(i);
if (!holder.wasReturnedFromScrap() && holder.getItemId() == id) {
holder.addFlags(ViewHolder.FLAG_RETURNED_FROM_SCRAP);
return holder;
}
}
}
}
return null;
}
ViewHolder getScrapViewForPosition(int position, int type, boolean dryRun) {
final int scrapCount = mAttachedScrap.size();
// Try first for an exact, non-invalid match from scrap.
for (int i = 0; i < scrapCount; i++) {
final ViewHolder holder = mAttachedScrap.get(i);
if (!holder.wasReturnedFromScrap() && holder.getPosition() == position
&& !holder.isInvalid() && (mState.mInPreLayout || !holder.isRemoved())) {
if (type != INVALID_TYPE && holder.getItemViewType() != type) {
Log.e(TAG, "Scrap view for position " + position + " isn't dirty but has" +
" wrong view type! (found " + holder.getItemViewType() +
" but expected " + type + ")");
break;
}
holder.addFlags(ViewHolder.FLAG_RETURNED_FROM_SCRAP);
return holder;
}
}
if (!dryRun) {
View view = mChildHelper.findHiddenNonRemovedView(position, type);
if (view != null) {
// ending the animation should cause it to get recycled before we reuse it
mItemAnimator.endAnimation(getChildViewHolder(view));
}
}
// Search in our first-level recycled view cache.
final int cacheSize = mCachedViews.size();
for (int i = 0; i < cacheSize; i++) {
final ViewHolder holder = mCachedViews.get(i);
if (!holder.isInvalid() && holder.getPosition() == position) {
if (!dryRun) {
mCachedViews.remove(i);
}
if (DEBUG) {
Log.d(TAG, "getScrapViewForPosition(" + position + ", " + type +
") found match in cache: " + holder);
}
return holder;
}
}
return null;
}
..............
}
..............
}
~~~
我们知道在**RecyclerView的Adapter 中被缓存的单位已经不是Item View 了, 而是一个View Holder** , 而原来的ListView 则是缓存的View 。在**RecyclerView. Recycler 类中有mAttachedScrap、mChangedScrap、mCachedViews 几个ViewHolder 列表对象**,它们就是**用于缓存ViewHolder** 的。
**在通过LayoutState 的next 函数获取Item View 时, 实际上调用的是RecyclerView.Recycler的getViewForPosition函数**, **该函数首先会从这几个ViewHolder 缓存中获取对应位置的ViewHolder,如果没有缓存则调用RecyclerView.Adapter中的createView Holder 函数来创建一个新的ViewHolder**,我们看看RecyclerView.Adapter 的createViewHolder 函数。
**RecyclerView.Adapter:createViewHolder()**
~~~
package android.support.v7.widget;
public class RecyclerView extends ViewGroup {
private static final String TAG = "RecyclerView";
..............
public static abstract class Adapter<VH extends ViewHolder> {
private final AdapterDataObservable mObservable = new AdapterDataObservable();
private boolean mHasStableIds = false;
............
public final VH createViewHolder(ViewGroup parent, int viewType) {
// 调用onCreateViewHolder 创建ViewHolder,用户需要覆写onCreateViewHolder函数
final VH holder = onCreateViewHolder(parent, viewType);
holder.mItemViewType = viewType;
return holder;
}
//创建ViewHolder,子类需覆写
public abstract VH onCreateViewHolder(ViewGroup parent, int viewType);
}
..............
}
~~~
在**createViewHolder 函数中实际上调用了onCreateViewHolder 函数创建ViewHolder 对象。这也就是为什么在继承RecyclerView.Adapter 时,需要覆写onCreateViewHolder 函数, 并且在该函数中返回ViewHolder 的原因**。
**通过这个onCreateViewHolder 函数会加载Item View视图, 并且把Item View 当作ViewHolder 的构造参数传递给ViewHolder(这个自定义的ViewHolder继承于RecyclerView.ViewHolder),此时ViewHolder 就构建完毕了**。调用了Adapter 的createViewHolder 后,此时执行到Recycler 的getViewForPosition 函数的注释4 处,也就是调用了Adapter 中的bindViewHolder 函数, 相关代码如下。
**RecyclerView.Adapter:bindViewHolder()**
~~~
public final void bindViewHolder(VH holder, int position) {
holder.mPosition = position;
if (hasStableIds()) {
holder.mItemId = getItemId(position);
}
//调用onBindViewHolder绑定数据
onBindViewHolder(holder, position);
//设置holder 的flags
holder.setFlags(ViewHolder.FLAG_BOUND,
ViewHolder.FLAG_BOUND | ViewHolder.FLAG_UPDATE | ViewHolder.FLAG_INVALID);
}
//绑定数据, 子类需覆写
public abstract void onBindViewHolder(VH holder, int position);
~~~
bindViewHolder 函数与createViewHolder 如出一辙,只是它调用的是onBindViewHolder 函数。与onCreateViewHolder 一样, **onBindViewHolder 也需要子类覆写, 并且在这个函数中进行数据绑定**。在getViewForPosition 中实际上相当于一个模板方法,它封装了获取、绑定ViewHolder 的过程,子类只需要覆写特定的函数即可完成这个过程。
执行完onBindViewHolder 函数之后数据就被绑定到Item View 上了。
我们最后再来分析一下这个过程。
与ListView 一样, **RecyclerView 还是通过Adapter 和观察者模式进行数据绑定,使得Recycler View 的灵活性得到保证**。**RecyclerView 的Adapter 并不是ListView 中的Adapter,它封装了ViewHolder 的创建与绑定等逻辑,降低了用户的使用成本。RecyclerView 也将缓存单元从Item View 换成了ViewHolder,在一定程度上建立起了规范**。
RecyclerView 与ListView **最大的不同是RecyclerView 将布局的工作交给了LayoutManager,在LayoutManager 的onLayou.tChilden 中对Item View 进行布局、执行动画等操作**,这样一来,**使得RecyclerView 可以动态地替换掉布局方式**,例如,在运行时给RecyclerView 重新设置一个LayoutManager 就可以将原来是线性布局的视图改成网格布局,这大大地增加了灵活性。将布局职责独立出来也符合单一职责原则,而使用组合代替继承也会减少精合、增强健壮性,也使得RecyclerView 的布局具有更好的扩展性。