RecycleView 源码解析
ListView 能局部刷新吗?
可以。。。
和ListView 的区别? 强在哪里?
更加灵活
怎么布局的? 怎么就实现了可以列表 和 网格的切换?
是怎样的回收和重复利用View?
不管recycleView 是何方神圣,最终都会调用到onMeasure、onLayout
androidx.recyclerview.widget.LinearLayoutManager#fill
RecycleView 缓存机制
1.从mAttachedScrap 里面获取
mAttachedScrap 好像没有被使用
真的没有看到这个的作用在哪里。好像没有用。
2.mCachedViews 获取
用于存放最常用的3个,包括将要显示的那个,预加载的,以及刚回收的
GapWorker 预创建View 完成之后就会放到cached 里面
"main"@12,125 in group "main": RUNNING
recycleViewHolderInternal(RecyclerView$ViewHolder):6487, RecyclerView$Recycler {androidx.recyclerview.widget}, RecyclerView.java
recycleView(View):6369, RecyclerView$Recycler {androidx.recyclerview.widget}, RecyclerView.java
prefetchPositionWithDeadline(RecyclerView, int, long):295, GapWorker {androidx.recyclerview.widget}, GapWorker.java
flushTaskWithDeadline(GapWorker$Task, long):345, GapWorker {androidx.recyclerview.widget}, GapWorker.java
flushTasksWithDeadline(long):361, GapWorker {androidx.recyclerview.widget}, GapWorker.java
prefetch(long):368, GapWorker {androidx.recyclerview.widget}, GapWorker.java
run():399, GapWorker {androidx.recyclerview.widget}, GapWorker.java
handleCallback(Message):873, Handler {android.os}, Handler.java
dispatchMessage(Message):99, Handler {android.os}, Handler.java
loop():193, Looper {android.os}, Looper.java
3.mRecyclerPool 获取
用于存在每一种类型的,5个。和CacheView 作用不太一样。
4.ViewCacheExtension获取
为什么做这么多层级的缓存?意义在哪里?
难道是这一层级的,不需要绑定数据?可以直接用?
5.GapWorker 到底是干什么用的? 怎么干的?
6.绑定ViewHolder之后什么时候添加到View树上的?
androidx.recyclerview.widget.LinearLayoutManager#layoutChunk
里面会调用addView
7.怎么实现的?滑动就增加view? 怎么实现这么流畅的?
onMeasure,onLayout 不会调用吗?(不会)
为什么?
父容器RecycleView 是不会重新调用onMeasure,onLayout的,但是会调用item 的 onMeasure 和 on Layout.
androidx.recyclerview.widget.LinearLayoutManager#layoutChunk
但是不调用invalidate 会刷新吗?
他不会去刷新layout。
8.requestLayout 和 invalidate 什么区别? 分别什么时候调用比较好?
invalidate 会导致重新布局吗?
requestLayout 会导致 onDraw 调用吗?
//invalidate 不会重新onLayout onMeasure
// invalidate();
// 重新布局 onMeasure onLayout onDraw 都会重新调用
requestLayout();
9.view 怎么 移动的 ? 肯定不是scroll, 应该是调用layout方法。
offsetTopAndBottom(int):15964, View {android.view}, View.java
offsetChildrenVertical(int):5042, RecyclerView {androidx.recyclerview.widget}, RecyclerView.java
offsetChildrenVertical(int):9136, RecyclerView$LayoutManager {androidx.recyclerview.widget}, RecyclerView.java
offsetChildren(int):369, OrientationHelper$2 {androidx.recyclerview.widget}, OrientationHelper.java
scrollBy(int, RecyclerView$Recycler, RecyclerView$State):1399, LinearLayoutManager {androidx.recyclerview.widget}, LinearLayoutManager.java
scrollVerticallyBy(int, RecyclerView$Recycler, RecyclerView$State):1128, LinearLayoutManager {androidx.recyclerview.widget}, LinearLayoutManager.java
scrollStep(int, int, int[]):1841, RecyclerView {androidx.recyclerview.widget}, RecyclerView.java
scrollByInternal(int, int, MotionEvent):1940, RecyclerView {androidx.recyclerview.widget}, RecyclerView.java
调用的offsetTopAndBottom 实现移动。调用requestLayout 其实会更改mTop 的,但是recycleView 没有问题是因为重写了onMeasure 方法,不会重新给mTop赋值。
10.offsetTopAndBottom 有什么优点?有什么作用?
快速移动? 不需要重新布局?只移动自己,不需要麻烦parent.
11.ainamator是怎么实现的?
12.具体的recycleView 的机制是什么?会提前加载上下一个列表项目吗?
13.滑动的时候,是先回收view还是先创建view 添加到底部?
首先,回收view, 然后添加下面的view,最后再回收view.
androidx.recyclerview.widget.RecyclerView#scrollBy
fill(RecyclerView$Recycler, LinearLayoutManager$LayoutState, RecyclerView$State, boolean):1576, LinearLayoutManager {androidx.recyclerview.widget}, LinearLayoutManager.java
scrollBy(int, RecyclerView$Recycler, RecyclerView$State):1391, LinearLayoutManager {androidx.recyclerview.widget}, LinearLayoutManager.java
scrollVerticallyBy(int, RecyclerView$Recycler, RecyclerView$State):1128, LinearLayoutManager {androidx.recyclerview.widget}, LinearLayoutManager.java
scrollStep(int, int, int[]):1841, RecyclerView {androidx.recyclerview.widget}, RecyclerView.java
scrollByInternal(int, int, MotionEvent):1940, RecyclerView {androidx.recyclerview.widget}, RecyclerView.java
onTouchEvent(MotionEvent):3391, RecyclerView {androidx.recyclerview.widget}, RecyclerView.java
static class LayoutState {static final String TAG = "LLM#LayoutState";static final int LAYOUT_START = -1;static final int LAYOUT_END = 1;static final int INVALID_LAYOUT = Integer.MIN_VALUE;static final int ITEM_DIRECTION_HEAD = -1;static final int ITEM_DIRECTION_TAIL = 1;static final int SCROLLING_OFFSET_NaN = Integer.MIN_VALUE;/*** We may not want to recycle children in some cases (e.g. layout)*/boolean mRecycle = true;/*** Pixel offset where layout should start* 应该接着哪里布局,接着那里添加view*/int mOffset;/*** Number of pixels that we should fill, in the layout direction.* 当前需要填充的长度* 可以为负数 如果是负数 说明滑动的距离 不足够划出下一个展示的View*/int mAvailable;/*** Current position on the adapter to get the next item.*/int mCurrentPosition;/*** Defines the direction in which the data adapter is traversed.* Should be {@link #ITEM_DIRECTION_HEAD} or {@link #ITEM_DIRECTION_TAIL}*/int mItemDirection;/*** Defines the direction in which the layout is filled.* Should be {@link #LAYOUT_START} or {@link #LAYOUT_END}*/int mLayoutDirection;/*** Used when LayoutState is constructed in a scrolling state.* It should be set the amount of scrolling we can make without creating a new view.* Settings this is required for efficient view recycling.* 滚动多少范围内,不需要创建新的view*/int mScrollingOffset;/*** Used if you want to pre-layout items that are not yet visible.* The difference with {@link #mAvailable} is that, when recycling, distance laid out for* {@link #mExtraFillSpace} is not considered to avoid recycling visible children.*/int mExtraFillSpace = 0;/*** Contains the {@link #calculateExtraLayoutSpace(RecyclerView.State, int[])} extra layout* space} that should be excluded for recycling when cleaning up the tail of the list during* a smooth scroll.*/int mNoRecycleSpace = 0;/*** Equal to {@link RecyclerView.State#isPreLayout()}. When consuming scrap, if this value* is set to true, we skip removed views since they should not be laid out in post layout* step.*/boolean mIsPreLayout = false;/*** The most recent {@link #scrollBy(int, RecyclerView.Recycler, RecyclerView.State)}* amount.*/int mLastScrollDelta;/*** When LLM needs to layout particular views, it sets this list in which case, LayoutState* will only return views from this list and return null if it cannot find an item.*/List<RecyclerView.ViewHolder> mScrapList = null;/*** Used when there is no limit in how many views can be laid out.*/boolean mInfinite;/*** @return true if there are more items in the data adapter*/boolean hasMore(RecyclerView.State state) {return mCurrentPosition >= 0 && mCurrentPosition < state.getItemCount();}/*** Gets the view for the next element that we should layout.* Also updates current item index to the next item, based on {@link #mItemDirection}** @return The next element that we should layout.*/View next(RecyclerView.Recycler recycler) {if (mScrapList != null) {return nextViewFromScrapList();}final View view = recycler.getViewForPosition(mCurrentPosition);mCurrentPosition += mItemDirection;return view;}/*** Returns the next item from the scrap list.* <p>* Upon finding a valid VH, sets current item position to VH.itemPosition + mItemDirection** @return View if an item in the current position or direction exists if not null.*/private View nextViewFromScrapList() {final int size = mScrapList.size();for (int i = 0; i < size; i++) {final View view = mScrapList.get(i).itemView;final RecyclerView.LayoutParams lp = (RecyclerView.LayoutParams) view.getLayoutParams();if (lp.isItemRemoved()) {continue;}if (mCurrentPosition == lp.getViewLayoutPosition()) {assignPositionFromScrapList(view);return view;}}return null;}public void assignPositionFromScrapList() {assignPositionFromScrapList(null);}public void assignPositionFromScrapList(View ignore) {final View closest = nextViewInLimitedList(ignore);if (closest == null) {mCurrentPosition = RecyclerView.NO_POSITION;} else {mCurrentPosition = ((RecyclerView.LayoutParams) closest.getLayoutParams()).getViewLayoutPosition();}}public View nextViewInLimitedList(View ignore) {int size = mScrapList.size();View closest = null;int closestDistance = Integer.MAX_VALUE;if (DEBUG && mIsPreLayout) {throw new IllegalStateException("Scrap list cannot be used in pre layout");}for (int i = 0; i < size; i++) {View view = mScrapList.get(i).itemView;final RecyclerView.LayoutParams lp = (RecyclerView.LayoutParams) view.getLayoutParams();if (view == ignore || lp.isItemRemoved()) {continue;}final int distance = (lp.getViewLayoutPosition() - mCurrentPosition)* mItemDirection;if (distance < 0) {continue; // item is not in current direction}if (distance < closestDistance) {closest = view;closestDistance = distance;if (distance == 0) {break;}}}return closest;}void log() {Log.d(TAG, "avail:" + mAvailable + ", ind:" + mCurrentPosition + ", dir:"+ mItemDirection + ", offset:" + mOffset + ", layoutDir:" + mLayoutDirection);}}
/*** The magic functions :). Fills the given layout, defined by the layoutState. This is fairly* independent from the rest of the {@link LinearLayoutManager}* and with little change, can be made publicly available as a helper class.** @param recycler Current recycler that is attached to RecyclerView* @param layoutState Configuration on how we should fill out the available space.* @param state Context passed by the RecyclerView to control scroll steps.* @param stopOnFocusable If true, filling stops in the first focusable new child* @return Number of pixels that it added. Useful for scroll functions.*/int fill(RecyclerView.Recycler recycler, LayoutState layoutState,RecyclerView.State state, boolean stopOnFocusable) {// max offset we should set is mFastScroll + available//记录最开始的时候 可用的空间final int start = layoutState.mAvailable;if (layoutState.mScrollingOffset != LayoutState.SCROLLING_OFFSET_NaN) {// TODO ugly bug fix. should not happenif (layoutState.mAvailable < 0) {//如果可用空间小于0 那么mScrollingOffset 就减去这个数layoutState.mScrollingOffset += layoutState.mAvailable;}recycleByLayoutState(recycler, layoutState);}int remainingSpace = layoutState.mAvailable + layoutState.mExtraFillSpace;LayoutChunkResult layoutChunkResult = mLayoutChunkResult;while ((layoutState.mInfinite || remainingSpace > 0) && layoutState.hasMore(state)) {layoutChunkResult.resetInternal();if (RecyclerView.VERBOSE_TRACING) {TraceCompat.beginSection("LLM LayoutChunk");}layoutChunk(recycler, state, layoutState, layoutChunkResult);if (RecyclerView.VERBOSE_TRACING) {TraceCompat.endSection();}if (layoutChunkResult.mFinished) {break;}layoutState.mOffset += layoutChunkResult.mConsumed * layoutState.mLayoutDirection;/*** Consume the available space if:* * layoutChunk did not request to be ignored* * OR we are laying out scrap children* * OR we are not doing pre-layout*/if (!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;}if (layoutState.mScrollingOffset != LayoutState.SCROLLING_OFFSET_NaN) {layoutState.mScrollingOffset += layoutChunkResult.mConsumed;if (layoutState.mAvailable < 0) {layoutState.mScrollingOffset += layoutState.mAvailable;}recycleByLayoutState(recycler, layoutState);}if (stopOnFocusable && layoutChunkResult.mFocusable) {break;}}if (DEBUG) {validateChildOrder();}return start - layoutState.mAvailable;}
回收View 代码块解读
private void recycleViewsFromStart(RecyclerView.Recycler recycler, int scrollingOffset,int noRecycleSpace) {if (scrollingOffset < 0) {if (DEBUG) {Log.d(TAG, "Called recycle from start with a negative value. This might happen"+ " during layout changes but may be sign of a bug");}return;}// ignore padding, ViewGroup may not clip children.final int limit = scrollingOffset - noRecycleSpace;final int childCount = getChildCount();if (mShouldReverseLayout) {for (int i = childCount - 1; i >= 0; i--) {View child = getChildAt(i);if (mOrientationHelper.getDecoratedEnd(child) > limit|| mOrientationHelper.getTransformedEndWithDecoration(child) > limit) {// stop hererecycleChildren(recycler, childCount - 1, i);return;}}} else {for (int i = 0; i < childCount; i++) {View child = getChildAt(i);//limit 表示滑动多少 就会创建新View//如果说你的底部小于limit 说明你肯定要被回收//直到找到一个View的底部>limit 的地方if (mOrientationHelper.getDecoratedEnd(child) > limit|| mOrientationHelper.getTransformedEndWithDecoration(child) > limit) {// stop hererecycleChildren(recycler, 0, i);return;}}}}
RecycleView 源码解析相关推荐
- Handler源码解析2
Handler源码解析1 https://blog.csdn.net/qq_44076155/article/details/110676740 Handler源码解析2 https://blog.c ...
- 谷歌BERT预训练源码解析(二):模型构建
版权声明:本文为博主原创文章,遵循 CC 4.0 BY-SA 版权协议,转载请附上原文出处链接和本声明. 本文链接:https://blog.csdn.net/weixin_39470744/arti ...
- 谷歌BERT预训练源码解析(三):训练过程
目录 前言 源码解析 主函数 自定义模型 遮蔽词预测 下一句预测 规范化数据集 前言 本部分介绍BERT训练过程,BERT模型训练过程是在自己的TPU上进行的,这部分我没做过研究所以不做深入探讨.BE ...
- 谷歌BERT预训练源码解析(一):训练数据生成
目录 预训练源码结构简介 输入输出 源码解析 参数 主函数 创建训练实例 下一句预测&实例生成 随机遮蔽 输出 结果一览 预训练源码结构简介 关于BERT,简单来说,它是一个基于Transfo ...
- Gin源码解析和例子——中间件(middleware)
在<Gin源码解析和例子--路由>一文中,我们已经初识中间件.本文将继续探讨这个技术.(转载请指明出于breaksoftware的csdn博客) Gin的中间件,本质是一个匿名回调函数.这 ...
- Colly源码解析——结合例子分析底层实现
通过<Colly源码解析--框架>分析,我们可以知道Colly执行的主要流程.本文将结合http://go-colly.org上的例子分析一些高级设置的底层实现.(转载请指明出于break ...
- libev源码解析——定时器监视器和组织形式
我们先看下定时器监视器的数据结构.(转载请指明出于breaksoftware的csdn博客) /* invoked after a specific time, repeatable (based o ...
- libev源码解析——定时器原理
本文将回答<libev源码解析--I/O模型>中抛出的两个问题.(转载请指明出于breaksoftware的csdn博客) 对于问题1:为什么backend_poll函数需要指定超时?我们 ...
- libev源码解析——I/O模型
在<libev源码解析--总览>一文中,我们介绍过,libev是一个基于事件的循环库.本文将介绍其和事件及循环之间的关系.(转载请指明出于breaksoftware的csdn博客) 目前i ...
最新文章
- 站覆盖范围_你了解无线覆盖范围和穿墙能力吗?别再走进这些误区了
- 怎样知道邮箱的端口_网络端口介绍
- MAC下homebre安装mysql
- 初学__Python——Python 自定义函数
- 前端Javascript与Nodejs的异同
- arcgis api for js入门开发系列十八风向流动图
- python中的class类使用方法[探索3]
- 二分法查找c语言程序_用C++写二分查找了!【手绘漫画】图解LeetCode之搜索插入位置(LeetCode 35)...
- 另菜单或工具栏按钮失效的mfc处理方法
- android中svn合并分支,Android Studio之SVN打分支、切换分支及合并分支
- python随机生成电话号码
- 知道如何防止域名被封,干货!赶紧收藏
- 你鼓舞了我是世界杯主题曲吗_20位鼓舞人心的科技女性
- Xposed原理分析
- Ceph Calamari软件包介绍
- 什么是“ Windows任务的主机进程”,为什么我的PC上运行着这么多主机?
- Sloan中性群落模型(NCM)推断群落构建原理及其R实现
- ICCV2021 | Vision Transformer中相对位置编码的反思与改进
- 云呐|机房监控服务平台,机房监控服务平台有哪些
- DVB误码率手持式场强仪科普说明