在事件分发的过程中,用于Android特有的事件分发机制,可能出现两个问题:
子View在滑动过程中产生requestDisallowTouchEvent()导致父View没有办法拦截到事件的问题(父View没有回调onInterceptTouchEvent())。
父View拦截事件并消耗,此时达到某个临界值,父View不再需要事件,但是子View无法从ACTION_MOVE事件直接开始处理,所以导致子View只能从下次事件序列开始处理。
问题一解决
我们只需重写在父ViewGroup重写requestDisallowTouchEvent(),不调用super的同名方法,因为super会在此ViewGroup放上标记,导致子ViewGroup没法办法继续拦截事件
@Override
public void requestDisallowInterceptTouchEvent(boolean disallowIntercept) {
//super.requestDisallowInterceptTouchEvent(disallowIntercept);
// 但是需要继续向上传递,否则ListView之类的控件很多状态会错误
getParent().requestDisallowInterceptTouchEvent(disallowIntercept);
}
问题二解决
我们在当前ViewGroup中处理Touch事件达到临界值时,自行补发ACTION_UP和ACTION_DOWN事件,让事件传递流程重启,这样就不会出现子View需要在下次事件序列才能重启的问题了。
// ViewGroup拦截到事件后进行处理
@Override
public boolean onTouchEvent(MotionEvent event) {
Log.d(TAG, "onTouchEvent: " + event);
final int action = event.getAction();
boolean needReset = false;
switch (action) {
case MotionEvent.ACTION_DOWN:
break;
case MotionEvent.ACTION_MOVE:
int offset = (int) (event.getY() - mLastY);
// 此时滑动到顶部,需要重新启动事件传递机制
if (mChild.getTop() + offset < mHoverY) {
offset = (int) (mHoverY - mChild.getTop());
needReset = true;
}
mChild.offsetTopAndBottom(offset);
if (needReset) {
// 重启ListView的事件机制
resetTouchEvent(event);
}
break;
}
mLastY = event.getY();
return super.onTouchEvent(event);
}
// 使子View恢复事件
private void resetTouchEvent(MotionEvent event) {
MotionEvent ev = MotionEvent.obtain(event);
ev.setAction(MotionEvent.ACTION_UP);
MotionEvent ev2 = MotionEvent.obtain(event);
ev.setAction(MotionEvent.ACTION_CANCEL);
MotionEvent ev3 = MotionEvent.obtain(event);
ev3.setAction(MotionEvent.ACTION_DOWN);
dispatchTouchEvent(ev);
dispatchTouchEvent(ev2);
dispatchTouchEvent(ev3);
ev.recycle();
ev2.recycle();
ev3.recycle();
}
当然也可是使用NestScroll机制进行解决,NestScroll机制没有上述的两种问题。
解决两个问题后,可以较为流畅的处理手势。