RecycleView Layout 详解
前提
我们以RecycleView 的宽高都是Match_Parent作为前提去分析,这样onMeasure方法就不会调用dispatchStep1、dispatchStep2.我们只看onLayout 方法
ViewHolder 一些属性
public abstract static class ViewHolder {@NonNullpublic final View itemView;WeakReference<RecyclerView> mNestedRecyclerView;int mPosition = NO_POSITION;int mOldPosition = NO_POSITION;long mItemId = NO_ID;int mItemViewType = INVALID_TYPE;//之前int mPreLayoutPosition = NO_POSITION;
mPreLayoutPosition 用于dispatchLayoutStep1,表示当前ViewHolder的位置,mPosition 用于dispatchLayoutStep2 时表示ViewHolder的位置。
getLayoutPosition
public final int getLayoutPosition() {return mPreLayoutPosition == NO_POSITION ? mPosition : mPreLayoutPosition;}
getLayoutPosition 如果当前是PreLayout阶段(dispatchLayoutStep1方法),那么该方法返回的就是mPreLayoutPosition,如果不是PreLayout阶段(dispatchLayoutStep2方法),就会返回mPosition.
首次进去RecycleView 页面
androidx.recyclerview.widget.RecyclerView#onLayout
@Overrideprotected void onLayout(boolean changed, int l, int t, int r, int b) {dispatchLayout();mFirstLayoutComplete = true;}
里面调用了dispatchLayout 方法
androidx.recyclerview.widget.RecyclerView#dispatchLayout
void dispatchLayout() {if (mLayout == null) {Log.e(TAG, "No layout manager attached; skipping layout");// leave the state in STARTreturn;}mState.mIsMeasuring = false;if (mState.mLayoutStep == State.STEP_START) {dispatchLayoutStep1();mLayout.setExactMeasureSpecsFrom(this);dispatchLayoutStep2();} else if (mAdapterHelper.hasUpdates() || mLayout.getWidth() != getWidth()|| mLayout.getHeight() != getHeight()) {// First 2 steps are done in onMeasure but looks like we have to run again due to// changed size.mLayout.setExactMeasureSpecsFrom(this);dispatchLayoutStep2();} else {// always make sure we sync them (to ensure mode is exact)mLayout.setExactMeasureSpecsFrom(this);}dispatchLayoutStep3();}
里面layout 会分成三步。
dispatchLayoutStep1
private void dispatchLayoutStep1() {mState.assertLayoutStep(State.STEP_START);mState.mIsMeasuring = false;//用于控制在layout期间 requestLayout不会生效startInterceptRequestLayout();//首次进去页面不会有任何处理 当我们调用notifyItemRemove 会修改oldpositionprocessAdapterUpdatesAndSetAnimationFlags();saveFocusInfo();mState.mTrackOldChangeHolders = mState.mRunSimpleAnimations && mItemsChanged;mItemsAddedOrRemoved = mItemsChanged = false;mState.mInPreLayout = mState.mRunPredictiveAnimations;mState.mItemCount = mAdapter.getItemCount();if (mState.mRunPredictiveAnimations) {clearOldPositions();} else {//清楚OldPosition PreLayoutPositionclearOldPositions();}onExitLayoutOrScroll();stopInterceptRequestLayout(false);mState.mLayoutStep = State.STEP_LAYOUT;}
第一步主要进行ViewHolder的一些数据更新操作,比如ViewHolder 某个元素被移除,那么后面的ViewHolder的Position 都会被减一,同时如果RecycleView 有动画,那么会进行PreLayout,拿到动画位置的信息。
dispatchLayoutStep2
private void dispatchLayoutStep2() {// Step 2: Run layoutmState.mInPreLayout = false;mLayout.onLayoutChildren(mRecycler, mState);mState.mStructureChanged = false;mPendingSavedState = null;}
真正的进行布局,如果在dispatchLayoutStep1进行过PreLayout,那么本次的布局所有的ViewHolder都是存在的,因为之前PreLayout 都已经进行了创建。
dispatchLayoutStep3
private void dispatchLayoutStep3() {mState.assertLayoutStep(State.STEP_ANIMATIONS);onEnterLayoutOrScroll();mState.mLayoutStep = State.STEP_START;if (mState.mRunSimpleAnimations) {//执行动画}mLayout.removeAndRecycleScrapInt(mRecycler);mState.mPreviousLayoutItemCount = mState.mItemCount;}
主要进行动画的处理,回收Scrap的ViewHolder到RecycleViewPool中,比如被移除掉的ViewHolder.
调用adapter.notifyItemRemoved()方法 onLayout执行分析
注意,本篇我们以有动画的RecyclerView 进行分析,如果RecyclerView 没有动画,那么不会执行PreLayout,也就是在dispatchLayoutStep1里面并不会进行布局的填充。
第一步,更新Position
at androidx.recyclerview.widget.RecyclerView$ViewHolder.offsetPosition(RecyclerView.java:11106)at androidx.recyclerview.widget.RecyclerView$ViewHolder.flagRemovedAndOffsetPosition(RecyclerView.java:11095)at androidx.recyclerview.widget.RecyclerView.offsetPositionRecordsForRemove(RecyclerView.java:4635)at androidx.recyclerview.widget.RecyclerView$6.offsetPositionsForRemovingLaidOutOrNewView(RecyclerView.java:996)at androidx.recyclerview.widget.AdapterHelper.postponeAndUpdateViewHolders(AdapterHelper.java:448)at androidx.recyclerview.widget.AdapterHelper.applyRemove(AdapterHelper.java:183)at androidx.recyclerview.widget.AdapterHelper.preProcess(AdapterHelper.java:102)at androidx.recyclerview.widget.RecyclerView.processAdapterUpdatesAndSetAnimationFlags(RecyclerView.java:3793)at androidx.recyclerview.widget.RecyclerView.dispatchLayoutStep1(RecyclerView.java:4039)at androidx.recyclerview.widget.RecyclerView.dispatchLayout(RecyclerView.java:3849)at androidx.recyclerview.widget.RecyclerView.onLayout(RecyclerView.java:4404)
在dispatchLayoutStep1 方法里面,会进行更新oldposition、preLayoutPostion,preLayoutPosition 有了之后,就可以执行dispatchLayoutStep1 里面的布局.
offsetPosition() 更新oldposition、preLayoutPostion
androidx.recyclerview.widget.RecyclerView.ViewHolder#offsetPositionvoid offsetPosition(int offset, boolean applyToPreLayout) {if (mOldPosition == NO_POSITION) {mOldPosition = mPosition;}if (mPreLayoutPosition == NO_POSITION) {mPreLayoutPosition = mPosition;}if (applyToPreLayout) {mPreLayoutPosition += offset;}mPosition += offset;if (itemView.getLayoutParams() != null) {((LayoutParams) itemView.getLayoutParams()).mInsetsDirty = true;}}
比如当一个item 移除掉,如果RecycleView 里面还有多余的元素,prelayout会加载没有下一个ViewHolder.
在布局的时候,移除的view 不会被计算占用的空间
androidx.recyclerview.widget.LinearLayoutManager#layoutChunk// Consume the available space if the view is not removed OR changedif (params.isItemRemoved() || params.isItemChanged()) {result.mIgnoreConsumed = true;}
androidx.recyclerview.widget.LinearLayoutManager#fillif (!layoutChunkResult.mIgnoreConsumed || layoutState.mScrapList != null|| !state.isPreLayout()) {layoutState.mAvailable -= layoutChunkResult.mConsumed;// we keep a separate remaining space because mAvailable is important for recyclingremainingSpace -= layoutChunkResult.mConsumed;}
当dispatchLayoutStep1 执行完毕,清除preLayoutPosition,进行dispatchLayoutStep2
androidx.recyclerview.widget.RecyclerView#dispatchLayoutStep1
androidx.recyclerview.widget.RecyclerView.ViewHolder#clearOldPosition
// we don't process disappearing list because they may re-appear in post layout pass.clearOldPositions();
dispatchLayoutStep3 回收被移除的ViewHolder
androidx.recyclerview.widget.RecyclerView.Recycler#recycleViewHolderInternalat androidx.recyclerview.widget.RecyclerView$Recycler.recycleViewHolderInternal(RecyclerView.java:6429)at androidx.recyclerview.widget.RecyclerView$Recycler.quickRecycleScrapView(RecyclerView.java:6554)at androidx.recyclerview.widget.RecyclerView$LayoutManager.removeAndRecycleScrapInt(RecyclerView.java:9249)at androidx.recyclerview.widget.RecyclerView.dispatchLayoutStep3(RecyclerView.java:4207)
总结:
首页进去RecycleView,只会在dispatchLayoutStep2 方法里面,对RecycleView 的布局进行填充。
如果我们调用了notifyItemRemoved 方法,在dispatchLayoutStep1 方法里面,会先更新OldPosition、PreLayoutPosition. 然后执行layout,layout之前会先调用mLayout.onLayoutChildren(mRecycler, mState);
在这个方法里面,会根据PreLayoutPosition 进行布局,因为有item 被移除,那么肯定会多出来空间,这时候,就会调用androidx.recyclerview.widget.LinearLayoutManager#fill
方法就会填充View,就会创建新的ViewHolder,在dispatchLayoutStep1 的最后,会把PreLayoutPosition 置为-1,这样就不影响dispatchLayoutStep2的布局。dispatchLayoutStep2 布局的时候,就会使用diapatchLayoutStep1创建的ViewHolder. 在dispatchStep3中,进行动画的处理,动画结束后,会把移除的ViewHolder 放到RecycleViewPool中重复利用。
RecycleView Layout 详解相关推荐
- Android recycleview使用详解,recycleview实现九宫格布局即横向排列,recycleview设置item占位数量大号item或小号item
1.添加recycleview依赖 compile('com.android.support:recyclerview-v7:25.1.1') {force = true } 2.item.xml & ...
- View 绘制体系知识梳理(4) 绘制过程之 Layout 详解
一.布局的起点 - performTraversals 和前面分析测量过程类似,整个布局的起点也是在ViewRootImpl的performTraversals当中: private void per ...
- [zz] Android五大布局Layout详解
ref: http://blog.csdn.net/llping2011/article/details/9992941?utm_source=tuicool Remark 最常用的:LinearL ...
- 【转载】RecycleView使用详解
原文链接:https://blog.csdn.net/u012721519/article/details/54692366 一.RecycleView简要介绍 RecycleView是support ...
- Android系统布局——android.R.layout详解
布局文件,作为android中必不可少的一部分,android系统为了方便开发人员,在系统中定义了很多的布局文件. 系统布局文件和我们自定义的布局在写法用前缀android以示区别: 系统布局文件:a ...
- recycleview 使用详解,添加头部尾部,混合item,侧滑菜单,跳转到指定位置,实现九宫格布局
添加头部尾部,混合item:https://blog.csdn.net/meixi_android/article/details/82256319 侧滑菜单:https://blog.csdn.ne ...
- View的绘制-layout流程详解
目录 作用 根据 measure 测量出来的宽高,确定所有 View 的位置. 具体分析 View 本身的位置是通过它的四个点来控制的: 以下涉及到源码的部分都是版本27的,为方便理解观看,代码有所删 ...
- idea2019配置gradle详解_Constraint Layout 2.0 用法详解
Constraint Layout 是最受欢迎的 Jetpack 库之一,它的 2.0 正式版本也发布啦 (目前最新版本 2.1.0-alpha1)!也许您已熟悉了 Constraint Layout ...
- android设置高度比例,android开发layout按比例布局(详解)
释放双眼,带上耳机,听听看~! 由于Android的复杂性,在写程序的时候经常会遇见一些难题,也可能会遇见处理不了的问题,下面是技术狗小编详解android layout 按比例布局的代码,一起进入下 ...
最新文章
- 解决安卓系统写入SD卡权限问题
- ES6 Rest参数
- VS2012简单的使用感受+插件推荐
- python简易图形-python图形用户界面(四):教你实现一个简单实用的计时器
- 【Redis】配置redis主从复制
- c++ 宽搜(倒水)
- 深入理解final关键字
- tornado学习笔记day03-响应输出
- 深度学习笔记(31) 迁移与增强
- python编程(基于twisted的客户端编程)
- 伪随机数与采样(sampling)
- android 虚拟键 高度,Android获取虚拟按键的高度(适配全面屏)
- 软件测试的类型有那些?都有什么不同?
- 1-丁基-3-甲基咪唑醋酸盐[Bmim][Ac]|离子液体1,1,3,3,-四甲基胍乳酸盐TMGL
- macbook android 屏幕共享,苹果设备小技巧:iPhone,iPad,Mac进行屏幕共享和远程控制...
- NCBI:Refseq
- python调用打印机参考例子_Python调用打印机参考例子
- js滑动操作之-pageYOffset,scrollTop,offsetHeight,scrollHeight
- 好累,好好思考今后要走的路
- UltraEdit 快捷键操作
热门文章
- python实现均值滤波_python如何实现均值滤波?
- html研究中心,seo研究中心 教程:认识HTML、css的重要性-专业SEO技
- python argparse库_python标准库之argparse
- python requests 示例_python的requests模块实现登陆示例
- [译] React Native vs. Cordova、PhoneGap、Ionic,等等
- OpenStack Pike Minimal安装:三、镜像管理
- C++ cin.ignore()用法
- 第四次Scrum编码冲刺!!!!
- Web开发者用什么编辑器?
- ATL CLR MFC Win32 常规 的区别