我们在处理滑动冲突的时候,无非都是遵循一个原则:
当用户想要操作里面那个视图的滑动功能时,让里面的视图处理掉滑动点击事件。
当用户想要操作外面那个视图的滑动功能时,让外面的视图处理掉滑动点击事件。
基于这两种场景,我们在处理滑动冲突的时候就有了外部解决法和内部解决法。
对于滑动方向不同的场景,用外部解决法比较容易写代码
对于滑动方向相同的场景,用内部解决法比较容易写代码
2.1 外部解决法
在外部视图的onInterceptTouchEvent进行逻辑判断,如果是父布局需要滑动,就拦截该事件,否则就放过该事件,代码如下:
override fun onInterceptTouchEvent(ev: MotionEvent?): Boolean {
when (ev?.action) {
MotionEvent.ACTION_DOWN -> {
return false
}
MotionEvent.ACTION_MOVE -> {
if (这是父布局的滑动事件) {
return true
} else {
return false
}
}
MotionEvent.ACTION_UP,
MotionEvent.ACTION_CANCEL -> {
return false
}
}
return super.onInterceptTouchEvent(ev)
}
2.2 内部拦截法
想要让子布局达成这个条件,就需要两点:
- 让子布局处理滑动逻辑的视图,确实的消化掉滑动事件,也就是让内部View处理dispatchTouchEvent这个方法最终return true。
- 让父布局跳过处理滑动逻辑的视图,不拦截该事件,好让事件确实的能流到内部的视图中而不是被外部视图直接处理,也就是让父布局的拦截方法onInterceptTouchEvent这个方法return false。
对于内部拦截法而言,由于他不直接修改父布局的onInterceptTouchEvent方法,所以他需要另外一个API:parent.requestDisallowInterceptTouchEvent(),用这个API来变相控制父布局的onInterceptTouchEvent返回true或false
同时要注意,由于点击事件的延续性,无论这个滑动事件最终是父布局处理还是子布局处理,最开始的DOWN事件父布局不要拦截,子布局在DOWN事件固定return true。
他的大体代码结构如下:
override fun dispatchTouchEvent(event: MotionEvent?): Boolean {
when(event?.action) {
MotionEvent.ACTION_DOWN -> {
parent.requestDisallowInterceptTouchEvent(true)
}
MotionEvent.ACTION_MOVE -> {
if (这是子布局的滑动事件)
parent.requestDisallowInterceptTouchEvent(true)
else
parent.requestDisallowInterceptTouchEvent(false)
}
MotionEvent.ACTION_CANCEL,
MotionEvent.ACTION_UP -> {
parent.requestDisallowInterceptTouchEvent(false)
}
}
// 如果是子布局的滑动事件,一定要保证该方法return true,这证明了子视图确实的消费了该点击事件
// 这样父布局才不会重复处理该事件,引起滑动冲突
// 由于点击事件的延续性,DOWN固定return true
if (这是子布局的滑动事件 || event?.action == MotionEvent.ACTION_DOWN)
super.dispatchTouchEvent(event)
return true
else
return super.dispatchTouchEvent(event)
}