可能是最詳細的Android點選事件處理詳解(三)
前兩篇文章:
可能是最詳細的Android點選事件處理詳解
可能是最詳細的Android點選事件處理詳解(二)
這裡再次延伸一下,在ScrollView和RecyclerView巢狀中touch事件的傳遞過程,以及巢狀滑動衝突的問題。
如上圖,外層是一個NestedScrollView,上半部分是一個400dp的RecyclerView。下面是用來填充的一些TextView。
本篇主要分四塊來講述:
1. 滑動上半部分的RecyclerView
2. 滑動下半部分的NestedScrollView
3. NestedScrollView內部只有RecyclerView滑動沒有Fling效果的情況
4. 處理上半部分RecyclerView滑動到底部,焦點在RecyclerView上滑動沒有Fling效果的情況
一,滑動上半部分的RecyclerView
日誌如下:
TouchScrollViewActivity[dispatchTouchEvent, 64]: ACTION_DOWN
NormalScrollView[dispatchTouchEvent, 35]: ACTION_DOWN
NormalScrollView[onInterceptTouchEvent, 61]: ACTION_DOWN
NormalRecyclerView[dispatchTouchEvent, 36]: ACTION_DOWN
NormalRecyclerView[onInterceptTouchEvent, 63]: ACTION_DOWN
NormalView[dispatchTouchEvent, 84]: ACTION_DOWN
NormalView$2[onTouch, 57]: ACTION_DOWN
NormalView[onTouchEvent, 111]: ACTION_DOWN
TouchScrollViewActivity[dispatchTouchEvent, 68]: ACTION_MOVE
NormalScrollView[dispatchTouchEvent, 39]: ACTION_MOVE
NormalScrollView[onInterceptTouchEvent, 65]: ACTION_MOVE
NormalRecyclerView[dispatchTouchEvent, 40]: ACTION_MOVE
NormalRecyclerView[onInterceptTouchEvent, 67]: ACTION_MOVE
NormalView[dispatchTouchEvent, 96]: ACTION_CANCEL
NormalView$2[onTouch, 68]: ACTION_CANCEL
NormalView[onTouchEvent, 122]: ACTION_CANCEL
TouchScrollViewActivity[dispatchTouchEvent, 68]: ACTION_MOVE
NormalScrollView[dispatchTouchEvent, 39]: ACTION_MOVE
NormalScrollView[onInterceptTouchEvent, 65]: ACTION_MOVE
NormalRecyclerView[dispatchTouchEvent, 40]: ACTION_MOVE
NormalRecyclerView[onTouchEvent, 94]: ACTION_MOVE
TouchScrollViewActivity[dispatchTouchEvent, 68]: ACTION_MOVE
NormalScrollView[dispatchTouchEvent, 39]: ACTION_MOVE
NormalRecyclerView[dispatchTouchEvent, 40]: ACTION_MOVE
NormalRecyclerView[onTouchEvent, 94]: ACTION_MOVE
TouchScrollViewActivity[dispatchTouchEvent, 72]: ACTION_UP
NormalScrollView[dispatchTouchEvent, 42]: ACTION_UP
NormalRecyclerView[dispatchTouchEvent, 43]: ACTION_UP
NormalRecyclerView[onTouchEvent, 97]: ACTION_UP
首先說下一下只有400dp高度的RecyclerView在滑動的時候是有Fling效果的。因為高度的限定,焦點在RecyclerView內,而且RecyclerView是可以滑動的,此時雖然ScrollView也是可以滑動,但是優先處理內部滑動,並且也有Fling效果,不存在衝突問題。因為系統已經幫我們處理了。
從日誌看跟我們可能是最詳細的Android點選事件處理詳解(二)
中滑動日誌是一樣的。只不過又多了一層ScrollView而已。這裡不做過多描述,重點在於此時的RecyclerView有Fling效果。
二,滑動下半部分的NestedScrollView
日誌如下:
TouchScrollViewActivity[dispatchTouchEvent, 64]: ACTION_DOWN
NormalScrollView[dispatchTouchEvent, 35]: ACTION_DOWN
NormalScrollView[onInterceptTouchEvent, 61]: ACTION_DOWN
NormalView[dispatchTouchEvent, 84]: ACTION_DOWN
NormalView$2[onTouch, 57]: ACTION_DOWN
NormalView[onTouchEvent, 111]: ACTION_DOWN
TouchScrollViewActivity[dispatchTouchEvent, 68]: ACTION_MOVE
NormalScrollView[dispatchTouchEvent, 39]: ACTION_MOVE
NormalScrollView[onInterceptTouchEvent, 65]: ACTION_MOVE
NormalView[dispatchTouchEvent, 89]: ACTION_MOVE
NormalView$2[onTouch, 61]: ACTION_MOVE
NormalView[onTouchEvent, 115]: ACTION_MOVE
TouchScrollViewActivity[dispatchTouchEvent, 68]: ACTION_MOVE
NormalScrollView[dispatchTouchEvent, 39]: ACTION_MOVE
NormalScrollView[onInterceptTouchEvent, 65]: ACTION_MOVE
NormalView[dispatchTouchEvent, 96]: ACTION_CANCEL
NormalView$2[onTouch, 68]: ACTION_CANCEL
NormalView[onTouchEvent, 122]: ACTION_CANCEL
TouchScrollViewActivity[dispatchTouchEvent, 68]: ACTION_MOVE
NormalScrollView[dispatchTouchEvent, 39]: ACTION_MOVE
NormalScrollView[onTouchEvent, 92]: ACTION_MOVE
TouchScrollViewActivity[dispatchTouchEvent, 72]: ACTION_UP
NormalScrollView[dispatchTouchEvent, 42]: ACTION_UP
NormalScrollView[onTouchEvent, 95]: ACTION_UP
首先描述一下此時的手指焦點是在ScrollView下面的View上面的此時滑動的是ScrollView是有Fling效果的。
從日誌看跟我們可能是最詳細的Android點選事件處理詳解(二)
中滑動日誌是一樣的。只不過是將RecyclerView換成了ScrollView。這裡不做過多描述,重點在於此時的ScrollView有Fling效果。因為內部的View不具備滑動效果。不存在衝突。
三,NestedScrollView內部只有RecyclerView滑動沒有Fling效果的情況
日誌如下:
TouchScrollViewActivity[dispatchTouchEvent, 64]: ACTION_DOWN
NormalScrollView[dispatchTouchEvent, 35]: ACTION_DOWN
NormalScrollView[onInterceptTouchEvent, 61]: ACTION_DOWN
NormalRecyclerView[dispatchTouchEvent, 36]: ACTION_DOWN
NormalRecyclerView[onInterceptTouchEvent, 63]: ACTION_DOWN
NormalView[dispatchTouchEvent, 84]: ACTION_DOWN
NormalView$2[onTouch, 57]: ACTION_DOWN
NormalView[onTouchEvent, 111]: ACTION_DOWN
TouchScrollViewActivity[dispatchTouchEvent, 68]: ACTION_MOVE
NormalScrollView[dispatchTouchEvent, 39]: ACTION_MOVE
NormalScrollView[onInterceptTouchEvent, 65]: ACTION_MOVE
NormalRecyclerView[dispatchTouchEvent, 40]: ACTION_MOVE
NormalRecyclerView[onInterceptTouchEvent, 67]: ACTION_MOVE
NormalView[dispatchTouchEvent, 96]: ACTION_CANCEL
NormalView$2[onTouch, 68]: ACTION_CANCEL
NormalView[onTouchEvent, 122]: ACTION_CANCEL
TouchScrollViewActivity[dispatchTouchEvent, 68]: ACTION_MOVE
NormalScrollView[dispatchTouchEvent, 39]: ACTION_MOVE
NormalScrollView[onInterceptTouchEvent, 65]: ACTION_MOVE
NormalRecyclerView[dispatchTouchEvent, 40]: ACTION_MOVE
NormalRecyclerView[onTouchEvent, 94]: ACTION_MOVE
TouchScrollViewActivity[dispatchTouchEvent, 72]: ACTION_UP
NormalScrollView[dispatchTouchEvent, 42]: ACTION_UP
NormalScrollView[onInterceptTouchEvent, 69]: ACTION_UP
NormalRecyclerView[dispatchTouchEvent, 43]: ACTION_UP
NormalRecyclerView[onTouchEvent, 97]: ACTION_UP
此時的滑動日誌其實和RecyclerView限制高度。滑動RecyclerView的日誌一樣,但是因為此時的ScrollView內部只有一個RecyclerView此時的ScollView和RecyclerView高度其實是一樣的。此時系統在滑動處理的時候,沒有幫助我們處理,此時滑動的時候從日誌看是作用到內部的RecyclerView了,但是此時的RecyclerView沒有Fling效果,即快速上滑螢幕,RecyclerView只會動一點,而不是慣性滾動很多。
對於這種情況其實很好解決,因為ScrollView和RecyclerView高度一樣。滑動內層和外層都是可以的。而一般處理衝突在外層處理比較方便一點。這裡又三種處理方式。
1. 在ScrollView層的onInterceptTouchEvent去攔截Move事件(這裡需要對一些細微move做一些處理,不然不容易出發touch到內層的事件),不讓內層的RecyclerView接收滑動事件,直接滑動ScrollView即可,此時在滑動就可以有Fling效果了。
2. recyclerView.setNestedScrollingEnabled(false);可以遮蔽RecyclerView的滑動,直接讓外層的ScrollView滑動。
3. 重寫LinearLayoutManager下的canScrollVertically()讓他返回false禁止豎向滑動。
以上三種方法均是遮蔽內層的滑動,讓外層進行滑動。
四,處理上半部分RecyclerView滑動到底部,焦點在RecyclerView上滑動沒有Fling效果的情況
首先我們知道,單獨滑動ScrollView和RecyclerView都是可以有Fling效果的,但是當RecyclerView和ScrollView同時滑動的時候,就不行了。這個時候我們要做的就是區分他們的滑動,因為系統以及幫我們處理了一部分。我們需要做的只是在臨界點去讓他們分別滑動。
提供如下思路:
1. 在外層ScrollView的onInterceptTouchEvent去攔截move事件
2. 當焦點在RecyclerView時,去判斷內層RecyclerView是否滑動到底部了。如果沒有滑動到底部,我們要讓它去滑動內層的RecyclerView。但是外層攔截了Move。我們需要呼叫getParent().requestDisallowInterceptTouchEvent(true);去駁回外層的Move攔截,讓他不去攔截Move。這個時候就可以滑動內層RecyclerView
3. 當RecyclerView滑動到底部的時候,這個時候如果焦點還在RecyclerView滑動就不會有Fling效果,我們呼叫getParent().requestDisallowInterceptTouchEvent(false);不去駁回外層攔截,讓他執行ScrollView的滑動。這個時候滑動ScrollView就有Fling效果了。
思路是這樣的。肯定可以。只不過有一些細節需要處理。程式碼後續我會補上。
專案原始碼: https://github.com/wangxp423/ViewExercise
在,首頁 -> Touch事件研究中。