RecyclerView 整体用到得设计模式有一下:
通过桥接模式,使RecyclerView 将布局方式独立成LayoutManager,实现对布局的定制化。
通过组合模式,使RecycleView通过dispatchLayout对Item View进行布局绘制的。
通过适配器模式,ViewHolder将RecycleView与ItemView联系起来,使得RecycleView方便操作ItemView。
通过观察者模式,给ViewHolder注册观察者,当调用notifyDataSetChanged时,就能重新绘制。
RecycleView得源码数据量非常多先列出几个核心点
RecyclerViewDataObserver 数据观察器
Recycler View循环复用系统,核心部件
SavedState RecyclerView状态
AdapterHelper 适配器更新
ChildHelper 管理子View
ViewInfoStore 存储子VIEW的动画信息
Adapter 数据适配器
LayoutManager 负责子VIEW的布局,核心部件
ItemAnimator Item动画
ViewFlinger 快速滑动管理
NestedScrollingChildHelper 管理子VIEW嵌套滑动
一般使用RecycleView得时候如下:
recyclerView = (RecyclerView) findViewById(R.id.recyclerView);
LinearLayoutManager layoutManager = new LinearLayoutManager(this);
//设置布局管理器
recyclerView.setLayoutManager(layoutManager);
//设置为垂直布局,这也是默认的
layoutManager.setOrientation(OrientationHelper. VERTICAL);
//设置Adapter
recyclerView.setAdapter( recycleAdapter);
//设置分隔线
recyclerView.addItemDecoration( new DividerGridItemDecoration(this ));
//设置增加或删除条目的动画
recyclerView.setItemAnimator( new DefaultItemAnimator());
看下RecycleView得构造函数
public RecyclerView(Context context, @Nullable AttributeSet attrs, int defStyle) {
super(context, attrs, defStyle);
setScrollContainer(true);
setFocusableInTouchMode(true);
final int version = Build.VERSION.SDK_INT;
mPostUpdatesOnAnimation = version >= 16;
final ViewConfiguration vc = ViewConfiguration.get(context);
mTouchSlop = vc.getScaledTouchSlop();
mMinFlingVelocity = vc.getScaledMinimumFlingVelocity();
mMaxFlingVelocity = vc.getScaledMaximumFlingVelocity();
setWillNotDraw(ViewCompat.getOverScrollMode(this) == ViewCompat.OVER_SCROLL_NEVER);
mItemAnimator.setListener(mItemAnimatorListener);
initAdapterManager();
initChildrenHelper();
// If not explicitly specified this view is important for accessibility.
if (ViewCompat.getImportantForAccessibility(this)
== ViewCompat.IMPORTANT_FOR_ACCESSIBILITY_AUTO) {
ViewCompat.setImportantForAccessibility(this,
ViewCompat.IMPORTANT_FOR_ACCESSIBILITY_YES);
}
mAccessibilityManager = (AccessibilityManager) getContext()
.getSystemService(Context.ACCESSIBILITY_SERVICE);
setAccessibilityDelegateCompat(new RecyclerViewAccessibilityDelegate(this));
// Create the layoutManager if specified.
if (attrs != null) {
int defStyleRes = 0;
TypedArray a = context.obtainStyledAttributes(attrs, R.styleable.RecyclerView,
defStyle, defStyleRes);
String layoutManagerName = a.getString(R.styleable.RecyclerView_layoutManager);
a.recycle();
createLayoutManager(context, layoutManagerName, attrs, defStyle, defStyleRes);
}
mScrollingChildHelper = new NestedScrollingChildHelper(this);
setNestedScrollingEnabled(true);
}
总体上进行了一系列得出事划后,createLayoutManager,创建了一个布局管理器,看下源码实现
/**
* Instantiate and set a LayoutManager, if specified in the attributes.
*/
private void createLayoutManager(Context context, String className, AttributeSet attrs,
int defStyleAttr, int defStyleRes) {
if (className != null) {
className = className.trim();
if (className.length() != 0) { // Can't use isEmpty since it was added in API 9.
className = getFullClassName(context, className);
try {
ClassLoader classLoader;
if (isInEditMode()) {
// Stupid layoutlib cannot handle simple class loaders.
classLoader = this.getClass().getClassLoader();
} else {
classLoader = context.getClassLoader();
}
Class extends LayoutManager> layoutManagerClass =
classLoader.loadClass(className).asSubclass(LayoutManager.class);
Constructor extends LayoutManager> constructor;
Object[] constructorArgs = null;
try {
constructor = layoutManagerClass
.getConstructor(LAYOUT_MANAGER_CONSTRUCTOR_SIGNATURE);
constructorArgs = new Object[]{context, attrs, defStyleAttr, defStyleRes};
} catch (NoSuchMethodException e) {
try {
constructor = layoutManagerClass.getConstructor();
} catch (NoSuchMethodException e1) {
e1.initCause(e);
throw new IllegalStateException(attrs.getPositionDescription() +
": Error creating LayoutManager " + className, e1);
}
}
constructor.setAccessible(true);
setLayoutManager(constructor.newInstance(constructorArgs));
} catch (ClassNotFoundException e) {
throw new IllegalStateException(attrs.getPositionDescription()
+ ": Unable to find LayoutManager " + className, e);
} catch (InvocationTargetException e) {
throw new IllegalStateException(attrs.getPositionDescription()
+ ": Could not instantiate the LayoutManager: " + className, e);
} catch (InstantiationException e) {
throw new IllegalStateException(attrs.getPositionDescription()
+ ": Could not instantiate the LayoutManager: " + className, e);
} catch (IllegalAccessException e) {
throw new IllegalStateException(attrs.getPositionDescription()
+ ": Cannot access non-public constructor " + className, e);
} catch (ClassCastException e) {
throw new IllegalStateException(attrs.getPositionDescription()
+ ": Class is not a LayoutManager " + className, e);
}
}
}
}
如果在布局里面设置了布局管理器得类型,那么会通过反射得反射实例化出对应得布局管理器,最后将实例化出得布局管理器设置到当前得RecycleView,看下setLayoutManager得实现
/**
* Set the {@link LayoutManager} that this RecyclerView will use.
*
* In contrast to other adapter-backed views such as {@link android.widget.ListView}
* or {@link android.widget.GridView}, RecyclerView allows client code to provide custom
* layout arrangements for child views. These arrangements are controlled by the
* {@link LayoutManager}. A LayoutManager must be provided for RecyclerView to function.
*
* Several default strategies are provided for common uses such as lists and grids.
*
* @param layout LayoutManager to use
*/
public void setLayoutManager(LayoutManager layout) {
if (layout == mLayout) {
return;
}
// TODO We should do this switch a dispachLayout pass and animate children. There is a good
// chance that LayoutManagers will re-use views.
if (mLayout != null) {
if (mIsAttached) {
mLayout.dispatchDetachedFromWindow(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.dispatchAttachedToWindow(this);
}
}
requestLayout();
}
设置布局管理器之前,清空所有得缓存得VIEW,最后通过VIEW刷新,调用requestLayout 进行刷新,看下实现
/**
* Calls {@code RecyclerView#requestLayout} on the underlying RecyclerView
*/
public void requestLayout() {
if(mRecyclerView != null) {
mRecyclerView.requestLayout();
}
}
@Override
public void requestLayout() {
if (!mEatRequestLayout && !mLayoutFrozen) {
super.requestLayout();
} else {
mLayoutRequestEaten = true;
}
}
调用的是ViewGroup得requestLayout,采用得是责任链模式,层层向上传递,最后传递到ViewRootImpl,然后重新调用了View得measure、layout、draw 方法来展示布局。看下源码。
public void requestLayout() {
if (mMeasureCache != null) mMeasureCache.clear();
if (mAttachInfo != null && mAttachInfo.mViewRequestingLayout == null) {
// Only trigger request-during-layout logic if this is the view requesting it,
// not the views in its parent hierarchy
ViewRootImpl viewRoot = getViewRootImpl();
if (viewRoot != null && viewRoot.isInLayout()) {
if (!viewRoot.requestLayoutDuringLayout(this)) {
return;
}
}
mAttachInfo.mViewRequestingLayout = this;
}
//为当前view设置标记位 PFLAG_FORCE_LAYOUT
mPrivateFlags |= PFLAG_FORCE_LAYOUT;
mPrivateFlags |= PFLAG_INVALIDATED;
if (mParent != null && !mParent.isLayoutRequested()) {
//向父容器请求布局
mParent.requestLayout();
}
if (mAttachInfo != null && mAttachInfo.mViewRequestingLayout == this) {
mAttachInfo.mViewRequestingLayout = null;
}
}
接着调用
@Override
public void requestLayout() {
if (!mHandlingLayoutInLayoutRequest) {
checkThread();
mLayoutRequested = true;
scheduleTraversals();
}
}
先检查当前是否在主线程,否则抛出异常,然后调用scheduleTraversals,看下源码。
void scheduleTraversals() {
if (!mTraversalScheduled) {
mTraversalScheduled = true;
mTraversalBarrier = mHandler.getLooper().getQueue().postSyncBarrier();
mChoreographer.postCallback(
Choreographer.CALLBACK_TRAVERSAL, mTraversalRunnable, null);
if (!mUnbufferedInputDispatch) {
scheduleConsumeBatchedInput();
}
notifyRendererOfFramePending();
pokeDrawLockIfNeeded();
}
}
调用了mTraversalRunnable。
final class TraversalRunnable implements Runnable {
@Override
public void run() {
doTraversal();
}
}
void doTraversal() {
if (mTraversalScheduled) {
mTraversalScheduled = false;
mHandler.getLooper().getQueue().removeSyncBarrier(mTraversalBarrier);
if (mProfile) {
Debug.startMethodTracing("ViewAncestor");
}
performTraversals();
if (mProfile) {
Debug.stopMethodTracing();
mProfile = false;
}
}
}
最后调用了ViewRootImpl得performTraversals方法,这个是View 工作得核心方法,measure、layout、draw得三大流程就在这里面。看下源码。
private void performTraversals() {
...
if (!mStopped) {
int childWidthMeasureSpec = getRootMeasureSpec(mWidth, lp.width); // 1
int childHeightMeasureSpec = getRootMeasureSpec(mHeight, lp.height);
performMeasure(childWidthMeasureSpec, childHeightMeasureSpec);
}
}
if (didLayout) {
performLayout(lp, desiredWindowWidth, desiredWindowHeight);
...
}
if (!cancelDraw && !newSurface) {
if (!skipDraw || mReportNextDraw) {
if (mPendingTransitions != null && mPendingTransitions.size() > 0) {
for (int i = 0; i < mPendingTransitions.size(); ++i) {
mPendingTransitions.get(i).startChangingAnimations();
}
mPendingTransitions.clear();
}
performDraw();
}
}
...
}
最终调用到了recyclerview的onMeature ,看下recyclerView.setLayoutManager(layoutManager),这里用到了桥接模式。
public LinearLayoutManager(Context context, int orientation, boolean reverseLayout) {
setOrientation(orientation);
setReverseLayout(reverseLayout);
}
/**
* @param context Current context, will be used to access resources.
* @param spanCount The number of columns or rows in the grid
* @param orientation Layout orientation. Should be {@link #HORIZONTAL} or {@link
* #VERTICAL}.
* @param reverseLayout When set to true, layouts from end to start.
*/
public GridLayoutManager(Context context, int spanCount, int orientation,
boolean reverseLayout) {
super(context, orientation, reverseLayout);
setSpanCount(spanCount);
}
GridLayoutManager继承LinearLayoutManager。看下onMeasure 是如何测量得。
@SuppressLint("WrongCall")
@Override
protected void onMeasure(int widthSpec, int heightSpec) {
if (mAdapterUpdateDuringMeasure) {
eatRequestLayout();
processAdapterUpdatesAndSetAnimationFlags();
if (mState.mRunPredictiveAnimations) {
// TODO: try to provide a better approach.
// When RV decides to run predictive animations, we need to measure in pre-layout
// state so that pre-layout pass results in correct layout.
// On the other hand, this will prevent the layout manager from resizing properly.
mState.mInPreLayout = true;
} else {
// consume remaining updates to provide a consistent state with the layout pass.
mAdapterHelper.consumeUpdatesInOnePass();
mState.mInPreLayout = false;
}
mAdapterUpdateDuringMeasure = false;
resumeRequestLayout(false);
}
if (mAdapter != null) {
mState.mItemCount = mAdapter.getItemCount();
} else {
mState.mItemCount = 0;
}
if (mLayout == null) {
defaultOnMeasure(widthSpec, heightSpec);
} else {
mLayout.onMeasure(mRecycler, mState, widthSpec, heightSpec);
}
mState.mInPreLayout = false; // clear
}
第一次会调用defaultOnMeasure来进行测量,否则会调用 mLayout.onMeasure(mRecycler, mState, widthSpec, heightSpec);,看下defaultOnMeasure得实现。
/**
* Used when onMeasure is called before layout manager is set
*/
private void defaultOnMeasure(int widthSpec, int heightSpec) {
final int widthMode = MeasureSpec.getMode(widthSpec);
final int heightMode = MeasureSpec.getMode(heightSpec);
final int widthSize = MeasureSpec.getSize(widthSpec);
final int heightSize = MeasureSpec.getSize(heightSpec);
int width = 0;
int height = 0;
switch (widthMode) {
case MeasureSpec.EXACTLY:
case MeasureSpec.AT_MOST:
width = widthSize;
break;
case MeasureSpec.UNSPECIFIED:
default:
width = ViewCompat.getMinimumWidth(this);
break;
}
switch (heightMode) {
case MeasureSpec.EXACTLY:
case MeasureSpec.AT_MOST:
height = heightSize;
break;
case MeasureSpec.UNSPECIFIED:
default:
height = ViewCompat.getMinimumHeight(this);
break;
}
setMeasuredDimension(width, height);
}
有三种测量模式: MeasureSpec.EXACTLY, MeasureSpec.AT_MOST , MeasureSpec.UNSPECIFIED,分别代表精确大小,不精确大小,最大值,通过 MeasureSpec.getMode 可以得到 mode 。mLayout.onMeasure 最终也是调用defaultOnMeasure来进行测量得。
接着看下onLayout 得源码。
@Override
protected void onLayout(boolean changed, int l, int t, int r, int b) {
eatRequestLayout();
TraceCompat.beginSection(TRACE_ON_LAYOUT_TAG);
dispatchLayout();
TraceCompat.endSection();
resumeRequestLayout(false);
mFirstLayoutComplete = true;
}
看下dispatchLayout 得实现
void dispatchLayout() {
.......
if (mState.mRunSimpleAnimations) {
// Step 0: Find out where all non-removed items are, pre-layout
mState.mPreLayoutHolderMap.clear();
mState.mPostLayoutHolderMap.clear();
int count = mChildHelper.getChildCount();
for (int i = 0; i < count; ++i) {
final ViewHolder holder = getChildViewHolderInt(mChildHelper.getChildAt(i));
if (holder.shouldIgnore() || (holder.isInvalid() && !mAdapter.hasStableIds())) {
continue;
}
final View view = holder.itemView;
mState.mPreLayoutHolderMap.put(holder, new ItemHolderInfo(holder,
view.getLeft(), view.getTop(), view.getRight(), view.getBottom()));
}
}
/Step 1
if (mState.mRunPredictiveAnimations) {
......
saveOldPositions();
......
}
......
// Step 2: Run layout
mState.mInPreLayout = false;
mLayout.onLayoutChildren(mRecycler, mState);
mState.mStructureChanged = false;
mPendingSavedState = null;
......
resumeRequestLayout(false);
mLayout.removeAndRecycleScrapInt(mRecycler);
mState.mPreviousLayoutItemCount = mState.mItemCount;
mDataSetHasChangedAfterLayout = false;
mState.mRunSimpleAnimations = false;
mState.mRunPredictiveAnimations = false;
onExitLayoutOrScroll();
mLayout.mRequestedSimpleAnimations = false;
if (mRecycler.mChangedScrap != null) {
mRecycler.mChangedScrap.clear();
}
mState.mOldChangedHolders = null;
if (didChildRangeChange(mMinMaxLayoutPositions[0], mMinMaxLayoutPositions[1])) {
dispatchOnScrolled(0, 0);
}
}
这里分为几个步骤,分别是记录Vew得状态,然后存在数组中,接着通过LayoutManager 去布局View,最后布局完成后,记录布局后得转态。dispatchLayout 太复杂了,看得有点懵了。
看下draw得实现
@Override
public void draw(Canvas c) {
super.draw(c);
final int count = mItemDecorations.size();
for (int i = 0; i < count; i++) {
mItemDecorations.get(i).onDrawOver(c, this, mState);
}
...
}
@Override
public void onDraw(Canvas c) {
super.onDraw(c);
final int count = mItemDecorations.size();
for (int i = 0; i < count; i++) {
mItemDecorations.get(i).onDraw(c, this, mState);
}
draw 比较简单,就是画出对应得布局在canvas上。