目的
了解ListView的滚动机制,在滚动过程中View如何添加到ListView。
实现过程
先简单介绍下ListView滚动的大概算法。
算法实现:
- ACTION_MOVE动作触发,滚出屏幕外的View添加进scrap views,并且从ListView里detach
- 还在屏幕上的View,向滚动方向移动滚动距离
- 填充views
- 修正多余的滚动距离
图示:
源码分析
下面分析ACTION_MOVE的方向为从下往上。
1.ACTION_MOVE动作触发
ListView没有重写onTouchEvent(MotionEvent ev)方法,追溯到父类AbsListView,最终调用关键的scrollIfNeeded(int x, int y, MotionEvent vtev)方法。
scrollIfNeeded(int x,int y,MotionEvent vtev)方法中有段代码:
1 | // No need to do all this work if we're not going to move anyway |
trackMotionScroll(int deltaY, int incrementalDeltaY)方法开始对ListView里的views进行处理。deltaY是ACTION_DOWN触发开始到现在移动的距离,incrementalDeltaY是每次ACTION_MOVE之间移动的距离。
2.滚动到屏幕外的views添加进scrap views
下面代码是从下往上滚动:
1 | ...... |
3.移动还在屏幕上views
1 | offsetChildrenTopAndBottom(incrementalDeltaY); |
4.填充views1
2
3
4final int absIncrementalDeltaY = Math.abs(incrementalDeltaY);
if (spaceAbove < absIncrementalDeltaY || spaceBelow < absIncrementalDeltaY) {
fillGap(down);
}
从上往下滑动,第一个child在屏幕外高度>absIncrementalDeltaY,不用填充view;从下往上滑动,最后一个child在屏幕外高度>absIncrementalDeltaY,不用填充view。
fillGap(boolean down)方法由ListView实现。分析往下方向填充的过程。1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
void fillGap(boolean down) {
final int count = getChildCount();
if (down) {
int paddingTop = 0;
if ((mGroupFlags & CLIP_TO_PADDING_MASK) == CLIP_TO_PADDING_MASK) {
paddingTop = getListPaddingTop();
}
final int startOffset = count > 0 ? getChildAt(count - 1).getBottom() + mDividerHeight :paddingTop;
fillDown(mFirstPosition + count, startOffset);
correctTooHigh(getChildCount());
}else{
......
}
}
5.修正多余滚动距离
fillDown(int pos, int nextTop)方法做了填充工作。填充完考虑下,如果滚动距离太大,可能最后一个view填充完后下面还有一段距离,这就需要后面的correctTooHigh(int childCount)来修正。
1 | private void correctTooHigh(int childCount) { |
上面代码的思路:
1.最后一个view的bottom小于end说明需要修正
2.如果需要修正
a.如果第一个view的绝对position=0,那么修正距离为mListPadding.top-firstTop
b.如果第一个view的绝对position>0,那么先将所有views向下移动bottomOffset,然后向上填充views,填充完可能上部有未填满部分,所以调用adjustViewsUpOrdown()向上平移。
1 | private void adjustViewsUpOrDown() { |
mStackFromBottom表示的是view数量不够的时候是吸顶还是吸底,默认为false。
上面的算法思路:第一个view的绝对position为0,并且delta大于0,那么所有views移动-delta。