Recycleview滑动分析

通过使用as的Profiler监听cpu的运行状况,我们可以更容易的了解到Recycleview滑动的执行过程。由下图可以看到,recycle view的滑动一次调用了:

1
2
3
4
5
6
7
8
9
RecyclerView.onTouchEvent()
RecyclerView.scrollByInternal()
RecyclerView.scrollStep()
LinearLayoutManager.scrollVerticallyBy()
LinearLayoutManager.scrollBy()
OrientationHelper.offsetChildren()
LayoutManager.offsetChildrenVertical()
RecyclerView.offsetChildrenVertical()
View.offsetTopAndBottom()

img1

获取Recycleview滑了多少距离得一个好方式:

1
2
3
4
5
6
7
public int getScollYDistance() {
LinearLayoutManager layoutManager = (LinearLayoutManager) this.getLayoutManager();
int position = layoutManager.findFirstVisibleItemPosition();
View firstVisiableChildView = layoutManager.findViewByPosition(position);
int itemHeight = firstVisiableChildView.getHeight();
return (position) * itemHeight - firstVisiableChildView.getTop();
}
img1

scrollByInternal

scrollStep

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
// Recycleview
void scrollStep(int dx, int dy, @Nullable int[] consumed) {
// 设置mInterceptRequestLayoutDepth++
startInterceptRequestLayout();
// 设置mLayoutOrScrollCounter++
onEnterLayoutOrScroll();

TraceCompat.beginSection(TRACE_SCROLL_TAG);
fillRemainingScrollValues(mState);

int consumedX = 0;
int consumedY = 0;
if (dx != 0) {
// consumedX代表这次recycleview滚动具体消耗调的滚动距离
consumedX = mLayout.scrollHorizontallyBy(dx, mRecycler, mState);
}
if (dy != 0) {
// consumedY代表这次recycleview滚动具体消耗调的滚动距离
// 在自定义LayoutManager的时候scrollVerticallyBy这个方法一定是要我们重写的
// 这是实现页面滚动的关键。这个方法的返回值也就是我们真正消耗掉的滚动距离
// 在LinearlayoutManger里面,
consumedY = mLayout.scrollVerticallyBy(dy, mRecycler, mState);
}

TraceCompat.endSection();
repositionShadowingViews();

onExitLayoutOrScroll();
stopInterceptRequestLayout(false);

if (consumed != null) {
consumed[0] = consumedX;
consumed[1] = consumedY;
}
}
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
// LinearLayoutManager
@Override
public int scrollVerticallyBy(int dy, RecyclerView.Recycler recycler,
RecyclerView.State state) {
if (mOrientation == HORIZONTAL) {
return 0;
}
return scrollBy(dy, recycler, state);
}
int scrollBy(int delta, RecyclerView.Recycler recycler, RecyclerView.State state) {
if (getChildCount() == 0 || delta == 0) {
return 0;
}
ensureLayoutState();
mLayoutState.mRecycle = true;
final int layoutDirection = delta > 0 ? LayoutState.LAYOUT_END : LayoutState.LAYOUT_START;
final int absDelta = Math.abs(delta);
updateLayoutState(layoutDirection, absDelta, true, state);
// 这里执行了很关键的 f
final int consumed = mLayoutState.mScrollingOffset
+ fill(recycler, mLayoutState, state, false);
if (consumed < 0) {
// consumed小于0代表我们的recycleview并没有消耗这次滑动
return 0;
}
final int scrolled = absDelta > consumed ? layoutDirection * consumed : delta;
// 这里会回调到recycleview里面执行offsetChildrenVertical方法。遍历全部的view执行offsetTopAndBottom方法。实现他们的位移变化。
// 既然这里是做了位移变化,那上面的fill方法是在干嘛?
mOrientationHelper.offsetChildren(-scrolled);
mLayoutState.mLastScrollDelta = scrolled;
return scrolled;
}