完美解決 Android 滑動衝突!

處理自定義 View 中的滑動衝突

對於大多數 Android 開發來說,處理滑動衝突好像很難,但實戰一下又發現,好像也挺簡單,因爲這個實際上是有套路可循的。基本就兩種方案:外部攔截法 && 內部攔截法。

2.1 外部攔截法

所謂外部攔截法,顧名思義,就是直接在父容器中直接攔截掉我們的滑動事件,讓其不能進入到子元素中,這似乎和我們 RecyclerView 嵌套 RecyclerView 時禁用內部 RecyclerView 滑動有那麼一絲相似之處,就是內部不處理就完事兒了。但細細品來又完全不一樣,這裏的外部攔截法會讓內部元素根本就收不到滑動事件。

這種方法明顯非常適合我們上面講的事件分發機制。我們在接收 ACTION_MOVE 事件的時候,直接通過使 onInterceptTouchEvent() 方法返回 true 來直接攔截掉事件就可以了,僞代碼想必大家也知道了:

override fun onInterceptTouchEvent(ev: MotionEvent?): Boolean {
    ev?.run {
        if (action == MotionEvent.ACTION_MOVE && 父容器需要點擊事件){
            return true
        }
    }
    return super.onInterceptTouchEvent(ev)
}
代碼很簡單,我們僅僅需要在事件 ACTION_MOVE 時去處理我們的邏輯就好了,當滿足我們的邏輯的時候,就攔截掉 ACTION_MOVE 事件給自己處理。

至於爲什麼不去攔截 ACTION_DOWN 和 ACTION_UP,想必大家也清楚了。上面說了,如果攔截了 ACTION_DOWN 事件,那後續的 ACTION_MOVE、ACTION_UP 等其它事件均不會在調用 onInterceptTouchEvent() 方法,會直接交給當前容器處理。而如果我們攔截掉 ACTION_UP 的話,肯定會導致子元素的點擊事件無法被處理,因爲大家肯定都知道一個點擊事件從 ACTION_DOWN 開始,從 ACTION_UP 結束,二者缺一不可。

2.2 內部攔截法

內部攔截法相對外部攔截法會複雜一些,所以我們通常來說,都更加推薦用外部攔截法進行處理。不過,內部攔截法依然有着它非常重要的地位,具體情況有可能會遇到。

內部攔截法的話,需要 requestDisallowInterceptTouchEvent() 方法的支持,這個方法是幹什麼的呢?顧名思義,請求是否不允許攔截事件,其接收一個 boolean 參數,表示是否不允許攔截。

我們直接重寫子元素的 dispatchTouchEvent() 方法,得到僞代碼如下:

override fun dispatchTouchEvent(ev: MotionEvent?): Boolean {
    ev?.run {
        when(action){
            MotionEvent.ACTION_DOWN -> parent.requestDisallowInterceptTouchEvent(true)
            MotionEvent.ACTION_MOVE ->{
                if(滿足需要讓外部容器攔截事件){
                    parent.requestDisallowInterceptTouchEvent(false)
                }
            }
        }
    }
    return super.dispatchTouchEvent(ev)
}
想必代碼也是非常簡單易懂的,我們給父容器的 requestDisallowInterceptTouchEvent() 傳遞的參數代表是否不允許其攔截事件,當參數爲 true 的時候代表不允許攔截,爲 false 的時候代表攔截。所以看起來和外部攔截法也就如出一轍了。

不過僅僅有這點修改還不夠,我們通過前面的理論基礎知道,當我們的父容器攔截掉 ACTION_DOWN 事件的時候,所有的事件都無法再傳遞到子元素中,自然也就不會調用上面我們寫的 dispatchTouchEvent() 方法了。所以我們在內部攔截法的時候還需要重寫父容器的 onInterceptTouchEvent() 方法。

override fun onInterceptTouchEvent(ev: MotionEvent?): Boolean {
    ev?.run {
        if (action == MotionEvent.ACTION_DOWN){
            return false
        }
    }
    return super.onInterceptTouchEvent(ev)
}
至此,基本介紹了兩種處理滑動衝突的解決方案,在自定義 View 的時候結合實際場景也就可以得心應手了。
 

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章