前言:这篇文章主要是针对上一篇做个补充。在写上一篇文章的时候,疑惑为什么每次滑动的时候RecyclerView.OnScrollListener的onScrollStateChanged只会在开始和结束的时候各调用一次,但是onScrolled方法会被回调很多次。看了下Recyclerview的源码,终于了解了大概原理,所以有了这篇文章。
正文:
手指从“按下-->滑动--> ... ->滑动 -->抬起”,一共有三类事件触发,分别是:
MotionEvent.ACTION_DOWN-->MotionEvent.ACTION_MOVE-->...->MotionEvent.ACTION_MOVE->MotionEvent.ACTION_UP
滑动监听器:
public abstract static class OnScrollListener {
public void onScrollStateChanged(@NonNull RecyclerView recyclerView, int newState){}
public void onScrolled(@NonNull RecyclerView recyclerView, int dx, int dy){}
}
当在Recyclerview页面上滑动时,会走到Recyclerview的onTouchEvent回调方法,其内部对各种触摸事件进行处理。下面分别说明:
1、MotionEvent.ACTION_DOWN
手指按下,回调onTouchEvent,并判断事件为MotionEvent.ACTION_DOWN,这里没有关于滑动监听器的逻辑,可以略过,其代码为:
@Override
public boolean onTouchEvent(MotionEvent e) {
switch (action) {
case MotionEvent.ACTION_DOWN: {
...
}
}
2、MotionEvent.ACTION_MOVE
开始滑动,回调onTouchEvent,并判断事件为MotionEvent.ACTION_MOVE,其内部逻辑为:
@Override
public boolean onTouchEvent(MotionEvent e) {
switch (action) {
case MotionEvent.ACTION_DOWN: {
...
break
}
case MotionEvent.ACTION_MOVE: {
...
if (mScrollState != SCROLL_STATE_DRAGGING) {
...
setScrollState(SCROLL_STATE_DRAGGING);
...
}
if (mScrollState == SCROLL_STATE_DRAGGING) {
if (scrollByInternal(
canScrollHorizontally ? dx : 0,
canScrollVertically ? dy : 0, e)) {
getParent().requestDisallowInterceptTouchEvent(true);
}
}
...
break
}
}
}
其中mScrollState初始值为SCROLL_STATE_IDLE
private int mScrollState = SCROLL_STATE_IDLE;
所以第一次进入到MotionEvent.ACTION_MOVE内部逻辑时,条件mScrollState != SCROLL_STATE_DRAGGING为true,所以设置mScrollState状态为SCROLL_STATE_DRAGGING。在setScrollState(SCROLL_STATE_DRAGGING)方法内部最后会调用到滑动监听器RecyclerView.OnScrollListener的onScrollStateChanged()方法。
setScrollState(SCROLL_STATE_DRAGGING) -> dispatchOnScrollStateChanged(state) ->mScrollListeners.get(i).onScrollStateChanged(this, state);
从第二个MotionEvent.ACTION_MOVE事件发生后,mScrollState = SCROLL_STATE_DRAGGING,所以会走if (mScrollState == SCROLL_STATE_DRAGGING) {}内部的逻辑。在这个内部最后有执行到RecyclerView.OnScrollListener的onScrolled()方法。
scrollByInternal() -> dispatchOnScrolled() -> mScrollListeners.get(i).onScrolled(this, hresult, vresult)
因为一次滑动,会触发很多个MotionEvent.ACTION_MOVE事件,所以OnScrollListener的onScrolled()方法会被调用很多次。
3、MotionEvent.ACTION_UP
抬起手指,触发MotionEvent.ACTION_UP事件,把mScrollState状态还原成最初状态SCROLL_STATE_IDLE。这样当再次按下手指滑动时,又从头开始啦。
@Override
public boolean onTouchEvent(MotionEvent e) {
switch (action) {
case MotionEvent.ACTION_DOWN: {
...
break
}
case MotionEvent.ACTION_MOVE: {
...
if (mScrollState != SCROLL_STATE_DRAGGING) {
...
setScrollState(SCROLL_STATE_DRAGGING);
...
}
if (mScrollState == SCROLL_STATE_DRAGGING) {
if (scrollByInternal(
canScrollHorizontally ? dx : 0,
canScrollVertically ? dy : 0, e)) {
getParent().requestDisallowInterceptTouchEvent(true);
}
}
...
break
}
case MotionEvent.ACTION_MOVE: {
...
setScrollState(SCROLL_STATE_IDLE);
...
}
}
}