Android View滑动冲突解决方案-2. 解决方案

时间:2024-07-11 07:10:32

我们在处理滑动冲突的时候,无非都是遵循一个原则:
当用户想要操作里面那个视图的滑动功能时,让里面的视图处理掉滑动点击事件。
当用户想要操作外面那个视图的滑动功能时,让外面的视图处理掉滑动点击事件。

基于这两种场景,我们在处理滑动冲突的时候就有了外部解决法和内部解决法。
对于滑动方向不同的场景,用外部解决法比较容易写代码
对于滑动方向相同的场景,用内部解决法比较容易写代码

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 内部拦截法

想要让子布局达成这个条件,就需要两点:

  1. 让子布局处理滑动逻辑的视图,确实的消化掉滑动事件,也就是让内部View处理dispatchTouchEvent这个方法最终return true。
  2. 让父布局跳过处理滑动逻辑的视图,不拦截该事件,好让事件确实的能流到内部的视图中而不是被外部视图直接处理,也就是让父布局的拦截方法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)
}