問題描述
最近項目中碰到一個應(yīng)用場景纫溃,就是豎向滑動的recyclerView A內(nèi)嵌套著橫向滑動的recyclerView B斤斧。并且recyclerView A和一個appbarLayout共同在一個CoordinatorLayout內(nèi)協(xié)作滑動还惠。然而這種復(fù)雜的嵌套在API 24下出bug了婚脱。如果觸摸在recyclerView B上豎向滑動耕捞,那么recyclerView A是可以正巢菝瑁滑動的,但是appbarLayout卻沒有任何響應(yīng),也就是這時候nestedScrolling失效了距贷。
執(zhí)行過程
很顯然在recyclerView B上豎向滑動其實是不會被B執(zhí)行的(因為B是橫向滑動的呀),那按道理豎向滑動就相當(dāng)于在recyclerView A上豎向滑動的呀吻谋,為啥AppBarlayout不響應(yīng)了忠蝗,也就是NestedScrolling失效了?這種應(yīng)用場景從邏輯上來看應(yīng)該是正常的漓拾,肯定是android的SDK代碼哪里出問題了阁最。
我們來復(fù)盤一下整個流程。
- A dispatchTouchEvent ACTION_DOWN
- A onInterceptTouchEvent ACTION_DOWN 沒有攔截
- B dispatchTouchEvent ACTION_DOWN
- B onInterceptTouchEvent ACTION_DOWN
- B onTouchEvent
- ACTION_DOWN流程結(jié)束
- A dispatchTouchEvent ACTION_MOVE
- A onInterceptTouchEvent ACTION_MOVE 攔截(因為是vertical的move)
- B dispatchTouchEvent ACTION_CANCEL 變成cancel因為上一步被攔截了
- [x] 沒有B onInterceptTouchEvent骇两,因為其已經(jīng)是攔截鏈的末尾了
- B onTouchEvent ACTION_CANCEL
- A 成為攔截鏈尾端速种,A.targetView = null
- ACTION_MOVE 流程結(jié)束
- A dispatchTouchEvent ACTION_MOVE
- [x] 沒有A onInterceptTouchEvent ACTION_MOVE 因為其已經(jīng)是攔截鏈末尾了
- A onTouchEvent ACTION_MOVE
- ACTION_MOVE流程結(jié)束
- 重復(fù)上述A dispatchTouchEvent ACTION_MOVE流程....
- A dispatchTouchEvent ACTION_UP
- A onTouchEvent ACTION_UP
- ACTION_UP流程結(jié)束
問題所在
我們知道NestedScrolling機(jī)制的具體操作執(zhí)行是在dispatchNestedPreScroll()調(diào)用的時候執(zhí)行的。而是否可以執(zhí)行是在startNestedScroll(nestedScrollAxis)中判斷的低千,stopNestedScroll()強(qiáng)行關(guān)閉所有嵌套滑動哟旗。
經(jīng)過查看recyclerView的源碼可以發(fā)現(xiàn),兩個函數(shù)調(diào)用的時機(jī)如下:
- dispatchNestedPreScroll()
- 在onTouchEvent中ACTION_MOVE的時候被調(diào)用
- startNestedScroll()
- 在onInterceptTouchEvent中ACTION_DOWN的時候被調(diào)用栋操,根據(jù)canXXScroll的軸來開啟/關(guān)閉嵌套滑動
- 在onTouchEvent中ACTION_DOWN的時候被調(diào)用闸餐,根據(jù)canXXScroll的軸來開啟/關(guān)閉嵌套滑動
- stopNestedScroll()
- 在onTouchEvent中MOTION_UP的時候被調(diào)用
- 在onTouchEvent中MOTION_CANCEL的時候被調(diào)用
有了這幾個我們結(jié)合上述的流程可以發(fā)現(xiàn),在B onTouchEvent ACTION_CANCEL的時候stopNestedScroll被調(diào)用矾芙,這時候嵌套滑動被關(guān)閉了舍沙。后續(xù)的A dispatchTouchEvent ACTION_MOVE在也沒有機(jī)會把嵌套滑動機(jī)制打開,也就是所有的ACTION_MOVE都不會觸發(fā)嵌套滑動剔宪,所以appbarLayout就死活不動了拂铡。
解決辦法
知道了原因,我們只要手工在特定節(jié)點再調(diào)用一次startNestedScroll(SCROLL_AXIS_VERTICAL)即可葱绒,比較粗暴的做法就是不管什么時候都開著嵌套滑動感帅。讓recyclerView B繼承一個自己的recyclerView如下:
public class MyRecyclerView extends RecyclerView {
//blabla....
@Override
public boolean dispatchTouchEvent(MotionEvent ev) {
boolean res = super.dispatchTouchEvent(ev);
startNestedScroll(SCROLL_AXIS_VERTICAL);
return res;
}
}