Recycleview基于滑动分析

既然要研究滑动那就要看RecycleviewonTouchEvent方法:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
public boolean onTouchEvent(MotionEvent e) {
switch (action) {
case MotionEvent.ACTION_MOVE: {
// scrollByInternal 实现recycleview滚动
if (scrollByInternal(
canScrollHorizontally ? dx : 0,
canScrollVertically ? dy : 0,
e)) {
getParent().requestDisallowInterceptTouchEvent(true);
}
// 实现预缓存,如果要预取的viewHolder调用过onBinderViewHolder方法,那么预取的viewHolder就保存到mCachedViews里面。如果没有调用过onBindViewHolder方法,则把这个viewHolder添加到mRecycleviewPool里面。
if (mGapWorker != null && (dx != 0 || dy != 0)) {
mGapWorker.postFromTraversal(this, dx, dy);
}
}
} break;
case MotionEvent.ACTION_UP: {
mVelocityTracker.addMovement(vtev);
eventAddedToVelocityTracker = true;
mVelocityTracker.computeCurrentVelocity(1000, mMaxFlingVelocity);
final float xvel = canScrollHorizontally
? -mVelocityTracker.getXVelocity(mScrollPointerId) : 0;
final float yvel = canScrollVertically
? -mVelocityTracker.getYVelocity(mScrollPointerId) : 0;
// fling实现惯性滑动
if (!((xvel != 0 || yvel != 0) && fling((int) xvel, (int) yvel))) {
setScrollState(SCROLL_STATE_IDLE);
}
resetScroll();
} break;
}
return true;
}

    看方法scrollByInternal

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
boolean scrollByInternal(int x, int y, MotionEvent ev) {
if (mAdapter != null) {
// 实现recycleview的滑动
scrollStep(x, y, mReusableIntPair);
}
if (!mItemDecorations.isEmpty()) {
invalidate();
}
if (!awakenScrollBars()) {
invalidate();
}
return consumedNestedScroll || consumedX != 0 || consumedY != 0;
}

void scrollStep(int dx, int dy, @Nullable int[] consumed) {
int consumedX = 0;
int consumedY = 0;
if (dx != 0) {
consumedX = mLayout.scrollHorizontallyBy(dx, mRecycler, mState);
}
if (dy != 0) {
consumedY = mLayout.scrollVerticallyBy(dy, mRecycler, mState);
}
if (consumed != null) {
consumed[0] = consumedX;
consumed[1] = consumedY;
}
}

    可以看到滑动事件交给了LayoutManagerscrollVerticallyByh和scrollHorizontallyBy

所以自定义LayoutManager的话如果支持滑动那么就需要重写这两个方法。现在来看下scrollVertically方法。

1
2
3
4
5
6
7
8
@Override
public int scrollVerticallyBy(int dy, RecyclerView.Recycler recycler,
RecyclerView.State state) {
if (mOrientation == HORIZONTAL) {
return 0;
}
return scrollBy(dy, recycler, state);
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
// 这个方法的每一行代码都是那么的重要。。。。
// 经过九九八十一难,我才看懂。
int scrollBy(int delta, RecyclerView.Recycler recycler, RecyclerView.State state) {
// delta就是每次的滚动值
mLayoutState.mRecycle = true;
final int layoutDirection = delta > 0 ? LayoutState.LAYOUT_END : LayoutState.LAYOUT_START;
final int absDelta = Math.abs(delta);
// 这个方法里面每个参数的赋值都很重要。看下面我的分析
// 这个方法最重要的事情就是给 mAvailable 和 mScrollingOffset赋值。
// 我们的mScrollingOffset的值也就是在这时候被改变了。
updateLayoutState(layoutDirection, absDelta, true, state);
// fill方法 是不是很眼熟。。。这个方法实现父视图对孩子的添加
final int consumed = mLayoutState.mScrollingOffset
+ fill(recycler, mLayoutState, state, false);
if (consumed < 0) {
return 0;
}
final int scrolled = absDelta > consumed ? layoutDirection * consumed : delta;
// 上面fill完成了布局
// offsetChildren实现将每个view进行平移。太棒了。
// 以后我也要这么干,太会了。
mOrientationHelper.offsetChildren(-scrolled);
mLayoutState.mLastScrollDelta = scrolled;
return scrolled;
}

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
// 用来保存最后一个view全部展示出来需要滑动的距离
// 或者是最上面一个view全部展示出来需要的距离。
// 以及这次滑动值requireSpace - 最后一个view全部展示出来需要滑动的距离 他们的值的状态
// 如果大于0代表这次滑动最后一个view可以全部展示出来,需要去添加一个新view进来了。
private void updateLayoutState(int layoutDirection, int requiredSpace,
boolean canUseExistingSpace, RecyclerView.State state) {
// 这里假设手指运动方向是从下往上 则 layoutDirection = LayoutState.LAYOUT_END
mLayoutState.mInfinite = resolveIsInfinite(); //fasle
mLayoutState.mLayoutDirection = layoutDirection; // 1
mReusableIntPair[0] = 0;
mReusableIntPair[1] = 0;
calculateExtraLayoutSpace(state, mReusableIntPair);// 现在看来啥也没干
int extraForStart = Math.max(0, mReusableIntPair[0]);
int extraForEnd = Math.max(0, mReusableIntPair[1]);
boolean layoutToEnd = layoutDirection == LayoutState.LAYOUT_END;
mLayoutState.mExtraFillSpace = layoutToEnd ? extraForEnd : extraForStart;
mLayoutState.mNoRecycleSpace = layoutToEnd ? extraForStart : extraForEnd;
int scrollingOffset;
if (layoutToEnd) {//这里为true
// 我没搞懂这里为啥要叠加recycleview的paddingBottom的值 咋叠加都是0
mLayoutState.mExtraFillSpace += mOrientationHelper.getEndPadding();
// 找到最底下的view
final View child = getChildClosestToEnd();
// 由于不是反向布局 这里的mItemDirection = 1
mLayoutState.mItemDirection = mShouldReverseLayout ? LayoutState.ITEM_DIRECTION_HEAD
: LayoutState.ITEM_DIRECTION_TAIL;
// 比如我们页面已经添加了12个子view,那么这个getPosition(child)的值就是11 然后 由于是下上滑动的那么mItemDirection的值就是1 结果显而易见 mLayoutState.mCurrentPositiond = 11 + 1
mLayoutState.mCurrentPosition = getPosition(child) + mLayoutState.mItemDirection;
// mOffset 的值 等于 当前这个view距离父视图底部的距离。bottom的值。
mLayoutState.mOffset = mOrientationHelper.getDecoratedEnd(child);
// calculate how much we can scroll without adding new children (independent of layout)
// mOrientationHelper.getDecoratedEnd(child) 由于这个child是最底部的这个子视图。所以这个获取的值就是最底下的view距离recycleview顶部的距离。
//mOrientationHelper.getEndAfterPadding()获取的是recycleview的高度-paddingBottom值
// 这两个一减代表最后一个字view全部出现到屏幕上需要移动的距离
scrollingOffset = mOrientationHelper.getDecoratedEnd(child)
- mOrientationHelper.getEndAfterPadding();

} else {
final View child = getChildClosestToStart();
mLayoutState.mExtraFillSpace += mOrientationHelper.getStartAfterPadding();
mLayoutState.mItemDirection = mShouldReverseLayout ? LayoutState.ITEM_DIRECTION_TAIL
: LayoutState.ITEM_DIRECTION_HEAD;
mLayoutState.mCurrentPosition = getPosition(child) + mLayoutState.mItemDirection;
mLayoutState.mOffset = mOrientationHelper.getDecoratedStart(child);
// 最上面一个view全部出现到屏幕上需要移动的距离
scrollingOffset = -mOrientationHelper.getDecoratedStart(child)
+ mOrientationHelper.getStartAfterPadding();
}
// requiredSpace的值就是滚动的值
mLayoutState.mAvailable = requiredSpace;
// 这个canUseExistingSpace的值等于true
if (canUseExistingSpace) {
// scrollingOffset代表最后一个view全部划出屏幕的距离
// mLayoutState.mAvailable 代表此次滑动需要的距离
// 两者一减: 如果结果>0 代表最后一个view已经全部出现在屏幕了,这个时候需要去添加一个新的view
// 如果减的<0,则代表最后一个视图还不足以全部出现,也就不用添加新的页面了
mLayoutState.mAvailable -= scrollingOffset;
}
// 设置为最后一个view全部展示出来需要的距离
mLayoutState.mScrollingOffset = scrollingOffset;
}
//比如这次滑动距离为 10 。 最后一个view全部展示出来需要的距离为
//那么:mAvailable = 10 - 20 = -10 mScrollingOffset = 20。

    进入fill方法来看下:

滑动的时候mScrollingOffset = 最后一个view全部展示出来的距离

mAvailable = 滑动距离 - mScrollingOffset

这个mAvailable > 0 代表这次的滑动需要去增加新的页面。

​ mAvailable < 0 代表这次的滑动不需要去增加新的页面。

如果小于0,他里面执行了 mScrollingOffset = mScrollingOffset - mAvailable 也就等于这次的滑动距离。然后根据这次的滑动距离,去判断需不需要对不可见的view进行回收。4

fill方法不仅执行了添加视图的操作也执行了回收的操作。回收的操作我放在另外一篇博客来讲。《Recycleview回收原理分析》

img-0
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
int fill(RecyclerView.Recycler recycler, LayoutState layoutState,
RecyclerView.State state, boolean stopOnFocusable) {
final int start = layoutState.mAvailable;
// 这个mScrollingOffset的值在触发滑动的时候会被赋值为
// 最后一个view全部展示在屏幕上需要的距离
// mScrollingOffset的值只有在滑动的时候才会赋值
if (layoutState.mScrollingOffset != LayoutState.SCROLLING_OFFSET_NaN) {
// 这里面只会在滚动的时候才会调用。
// 小于0代表这次的滑动不足以让最下面的视图全部展示出来,
// 也就说明,我们不需要添加新的view到父视图里面来。
if (layoutState.mAvailable < 0) {
// 当小于0的时候这一步骤的操作无异于就是让
// mScrollingOffset的值 等于 这次滑动的值。
layoutState.mScrollingOffset += layoutState.mAvailable;
}
// 这是回收那些不可见的视图。这个方法很重要,回收的操作就在这里进行的。
// 回收的条件就是上面的mScrollingOffset这个方法,在回收的时候有一个基准线limit 他的值等于 mScrollingOffset
// 然后会循环遍历recycleview所有的子view,找到孩子view底部距离顶部的值 > limit 的所有符合条件的view。然后将他们
// 从父视图进行移除。 具体讲解参考我的博客: 《Recycleview回收原理分析》
recycleByLayoutState(recycler, layoutState);
}
int remainingSpace = layoutState.mAvailable + layoutState.mExtraFillSpace;
LayoutChunkResult layoutChunkResult = mLayoutChunkResult;
// remainSpace > 0 表示 有可用空间可以使用,那么为了填补这个可用空间,我们就需要执行addVie操作,不然怎么填补这部分空间,
// 你说是吧。 添加结束的标志就是 可用空间被填满。
while ((layoutState.mInfinite || remainingSpace > 0) && layoutState.hasMore(state)) {
// 这个方法就是加载一个子view
layoutChunk(recycler, state, layoutState, layoutChunkResult);
if (layoutChunkResult.mFinished) {
break;
}
layoutState.mOffset += layoutChunkResult.mConsumed * layoutState.mLayoutDirection;
// mIgnoreConsumed看名之意:忽略消耗。一般都不会忽略。
if (!layoutChunkResult.mIgnoreConsumed || layoutState.mScrapList != null
|| !state.isPreLayout()) {
// layoutChunkResult.mConsumed 的值就等于 一个itemView所需要消耗的高度。
layoutState.mAvailable -= layoutChunkResult.mConsumed;
remainingSpace -= layoutChunkResult.mConsumed;
}
// 这一段和上面那一段代码一样。
if (layoutState.mScrollingOffset != LayoutState.SCROLLING_OFFSET_NaN) {
layoutState.mScrollingOffset += layoutChunkResult.mConsumed;
if (layoutState.mAvailable < 0) {
// 这一步的操作无疑就是让mScrollingOffset的值 = 这次移动的值。
layoutState.mScrollingOffset += layoutState.mAvailable;
}
// 回收那些view底部距离recycleview顶部的值 大于 这次滑动的值的页面。
recycleByLayoutState(recycler, layoutState);
}
if (stopOnFocusable && layoutChunkResult.mFocusable) {
break;
}
}
// 当我们还有可以展示的更过的孩子的时候这个mAvailable的值一定是负数的。
return start - layoutState.mAvailable;
}

    现在关于onTouchevent里面的scrollByInternal方法讲解完了,现在看onTouchevent下的mGapWorker.postFromTraversal:

1
2
3
4
5
6
7
8
9
10
11
12

// GapWorker是一个继承子Runnable的类,也就是说通过post方法我们可以把这条消息发送出去
void postFromTraversal(RecyclerView recyclerView, int prefetchDx, int prefetchDy) {
if (recyclerView.isAttachedToWindow()) {
if (mPostTimeNs == 0) {
mPostTimeNs = recyclerView.getNanoTime();
// 通过post的方法把消息发送出去了。就需要看run方法。
recyclerView.post(this);
}
}
recyclerView.mPrefetchRegistry.setPrefetchVector(prefetchDx, prefetchDy);
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
// GapWorker.java
@Override
public void run() {
try {
if (mRecyclerViews.isEmpty()) {
return;
}
// 看名字就知道 预取
// 为啥预取。就是为了创建下一个viewHolder然后保存到缓存里面。
prefetch(nextFrameNs);
} finally {
mPostTimeNs = 0;
TraceCompat.endSection();
}
}
void prefetch(long deadlineNs) {
buildTaskList();
flushTasksWithDeadline(deadlineNs);
}
private void flushTasksWithDeadline(long deadlineNs) {
for (int i = 0; i < mTasks.size(); i++) {
final Task task = mTasks.get(i);
if (task.view == null) {
break;
}
flushTaskWithDeadline(task, deadlineNs);
task.clear();
}
}
//用最后期限刷新任务
private void flushTaskWithDeadline(Task task, long deadlineNs) {
long taskDeadlineNs = task.immediate ? RecyclerView.FOREVER_NS : deadlineNs;
//prefetchPositionWithDeadline 通过名字可以知道 是通过指定位置去预取
// 这个方法的内部就调用了我们在fill里面看到的next方法获取view的方法。就是tryGetViewHolderForPositionByDeadline方法。从recycer的四重缓存里面,寻找可以使用的viewHolder。
RecyclerView.ViewHolder holder = prefetchPositionWithDeadline(task.view,
task.position, taskDeadlineNs);
if (holder != null
&& holder.mNestedRecyclerView != null
&& holder.isBound()
&& !holder.isInvalid()) {
prefetchInnerRecyclerViewWithDeadline(holder.mNestedRecyclerView.get(), deadlineNs);
}
}

private RecyclerView.ViewHolder prefetchPositionWithDeadline(RecyclerView view,
int position, long deadlineNs) {
if (isPrefetchPositionAttached(view, position)) {
return null;
}

RecyclerView.Recycler recycler = view.mRecycler;
RecyclerView.ViewHolder holder;
try {
view.onEnterLayoutOrScroll();
// 这个方法就是从缓存里面获取多应得viewHolder
holder = recycler.tryGetViewHolderForPositionByDeadline(
if (holder != null) {
//isBound得意思就是判断这个viewHolder是否调用过onBinderViewHolder方法。
if (holder.isBound() && !holder.isInvalid()) {
recycler.recycleView(holder.itemView);
} else {
recycler.addViewHolderToRecycledViewPool(holder, false);
}
}
} finally {
view.onExitLayoutOrScroll(false);
}
return holder;
}

        看下prefetchPositionWithDeadline方法,里面判断viewHolder是否调用过onBinderViewHolder方法,如果调用过了,就把这个viewHolder添加到mCachedViews数组里面,如果没有添加过的话就添加到mRecycleviewPool里面。并且mRecycleviewPool里面的这种类型的数组还没有超过数组上线默认一种类型的数组大小是5:

1
2
3
4
5
6
7
8
9
10
11
12
// 这里先判断这个viewHolder是否调用过onBindViewHolder方法。onBindViewHolder方法就是把数据绑定到了view上面。
// recycle得缓存机制里面。mCachedViews这个数组是用来缓存那些调用过onBinderViewHolder方法得viewHolder。而mRecycleviwPool里面得mScrap数组保存的是从mCachedViews数组里面踢出来得viewHolder。也是那些没有调用过onBindViewHolder方法的视图。
if (holder != null) {
if (holder.isBound() && !holder.isInvalid()) {
// 如果绑定过视图了,就保存到mCachedViews数组里面,这样的话下次就可以直接展示了
recycler.recycleView(holder.itemView);
} else {
// 添加到mRecycleviewPoold的mScrap里面。
// 不过如果对应的veiwType的数组大于了最大限制默认是5.那么也不会往里面加了。
recycler.addViewHolderToRecycledViewPool(holder, false);
}
}

     关于滑动的时候的预取操作讲完了。


    在来看下在onTouchEvent里面的scrollByInternal方法。是怎么实现滑动 的。

     当手指从下往上的时候mLayoutState.mLayoutDirection = LayoutState.LAYOUT_END这个mLayoutState.mLayoutDirection的值是根据每次滑动的值来判断的。滑动改变值>0等于LAYOUT_END ;<0 等于LAYOUT_START。mLayoutState.mItemDirection是我们初始化的时候就已经设置好的值。不会变。


监听viewHolder移除

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21


// 这个方法是在fill方法里面,移除缓存里面的数据的时候调用。这个方法里面最终还是会调用下面的dispatchChildDetached方法。
public void removeAndRecycleViewAt(int index, @NonNull Recycler recycler) {
final View view = getChildAt(index);
removeViewAt(index);
recycler.recycleView(view);
}

void dispatchChildDetached(View child) {
final ViewHolder viewHolder = getChildViewHolderInt(child);
onChildDetachedFromWindow(child);
if (mAdapter != null && viewHolder != null) {
mAdapter.onViewDetachedFromWindow(viewHolder);
}
if (mOnChildAttachStateListeners != null) {
final int cnt = mOnChildAttachStateListeners.size();
for (int i = cnt - 1; i >= 0; i--) { mOnChildAttachStateListeners.get(i).onChildViewDetachedFromWindow(child);
}
}
}

方式一:

调用Recycleview.setRecyclerListener注册回收监听,这个回调里面可以拿到对应的viewHolder

调用Recycleveiw.addOnChildAttachStateChangeListener注册监听。这个方法只能拿到view.

重写Recycleview.onChildDetachedFromWindow,这个方法只能拿到view.

方式二:

重写Adapter.onViewRecycled可以监听到视图的回收,这个回调里面可以拿到对应的viewHolder

重写Adapter.onViewDetachedFromWindow

监听viewHolder添加

Recycleview:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
// 当一个viewHolder被添加的时候这个方法会被调用
void dispatchChildAttached(View child) {
final ViewHolder viewHolder = getChildViewHolderInt(child);
onChildAttachedToWindow(child);
if (mAdapter != null && viewHolder != null) {
mAdapter.onViewAttachedToWindow(viewHolder);
}
if (mOnChildAttachStateListeners != null) {
final int cnt = mOnChildAttachStateListeners.size();
for (int i = cnt - 1; i >= 0; i--) {
mOnChildAttachStateListeners.get(i).onChildViewAttachedToWindow(child);
}
}
}

方式一:

重写Adapter.onViewAttachedToWindow>方法。

方式二:

调用Recycleveiw.addOnChildAttachStateChangeListener注册监听。

可以发现addOnChildAttachStateChangeListener方法可以监听移除和添加

1
2
3
4
5
6
public interface OnChildAttachStateChangeListener {
// 这个方法是在viewGroup执行完 addView操作之后调用
void onChildViewAttachedToWindow(@NonNull View view);
// 这个方式是在ViewGroup.removeViewAt之前执行的。所以你在这个方法里面调用getChildCOunt的值还是
void onChildViewDetachedFromWindow(@NonNull View view);
}

如果要实现像抖音那样选中的视频要进行播放的话,该怎么做。

要实现一页一页滑动的话,当然用的是PagerSnapHelper工具类。

可以重写RecycleviewonScrollStateChanged方法监听滑动状态,当状态为SCROLL_STATE_IDLE的时候表示滑动停止。这样我们就可以在这个状态下获取当前展示在屏幕上的view。

让Recycleview滚动到指定位置

在看PagerSnapHelper的源码发现。

在实现惯性滑动的时候,recycleview的底层是通过这样的方法让recycleview滚动起来的。:

  1. 首先我们需要new一个Recycleview.SmoothScroller对象
  2. 给Recycleview.SmoothScroller对象设置一个目标滚动位置
  3. 调用layoutManager的startSmoothScroll(RecycleView.SmaoothScroller)方法
1
2
3
4
5
6
7
8
9
10
11
// 这部分全部代码在 SnaperHelper的 snapFromFling()方法里面。
// 创建Recycleview.SmoothScroller对象
RecyclerView.SmoothScroller smoothScroller = createScroller(layoutManager);
if (smoothScroller == null) {
return false;
}
int targetPosition = 10
// 设置滚动到的位置
smoothScroller.setTargetPosition(targetPosition);
// 然recycleview滚动起来
layoutManager.startSmoothScroll(smoothScroller);

Recycleview的缓存过程

首先先从mAttachedScrap里面获取,他代表的是展示在手机屏幕上面的viewHolder。mAttachesScrop在我们第一次布局页面的时候,虽然调用了他的add操作。但是那个时候recycleview是空的,所以mAttachedScrap数组也是空的。在我看来,当旋转屏幕的时候,系统会自动触发requestLayout。这个时候,我们的mAttachedScrop的数组就不在是空的。

在当mAttachedScrap为空的时候,就从mHiddenViews数组里面获取。

如果mHiddenViews为空,就从mCachedViews里面获取。这个mCachedViews的数组默认大小为2。意味着最大缓存为2,当超过2的时候,mCachedViews会移除超出的数据把他添加到RecycledViewPool里面。RecyclePool里面的数组是根据viewHolder的类型来保存的。每个类型的最大缓存数量为5。

mCachedViews为空的话就去RecycledViewPool里找,再为空就调用onCreateViewHolder去创建。

那mCachedViews里面的数据是在什么时候添加进去的呢?是在移动Recycleview的时候,然触发滑动的时候,如果有的页面超过了我们的限定线limit的时候,就会执行移除操作被添加到mCachedViews数组里面。

那RecycleredViewPool里的数据什么时候加的呢?再手势滑动的时候,执行move操作会调用recycleview的预取操作。预取操作里面判断想获取的那个view有没有执行过onBindViewHolder操作。执行过的那么就把预取得viewHolder加到mCachedViews里面。没有执行过onBindViewHolder操作得就放到RecycledViewPool里面。

通过这里可以知道,从mCachedViews里面获取得viewHolder可以直接拿来展示,而从RecycledViewPool里面获取得viewHolder需要再次执行onBindViewHodler操作。

Recycleview缓存过程知道了,那么他Recycleview从缓存里面取的条件是什么呢?

条件是:先遍历mCachedViews数组通过position找到和mCachedViews里面对应的那个viewHolder,mCachedViews里面保存的是最近移除屏幕的view,所以可以通过position找到那个最近移除的viewholder然后直接就可以展示出来,不需要走onBindViewHolder操作。如果在mCachedViews里面找不到就去RecycleredViewPool里面找,遍历RecycleredViewPool是不根据position来找的,而是根据viewHolder的type值来找,找到的话就把RecycleredViewPool数组里面的这个viewHolder删掉,然后在把删掉的这个返回回去。然后则个viewHolder是需要执行onBindViewHolder操作的。然后才能进行展示。

RecycleView更新

需要跟新得时候,会把数据加到:mPendingUpdates里面。

mAdapterUpdateDuringMeasure = true