BottomSheetBehavior下ViewPager2的滑動(dòng)問(wèn)題
該布局結(jié)構(gòu)下谣沸,ViewPager2下的RecyclerView無(wú)法響應(yīng)滑動(dòng)祈噪。
問(wèn)題點(diǎn)
現(xiàn)象:BottomSheetBehavior可以正常折疊台妆,展開(kāi)绽榛,收起瞭郑。展開(kāi)狀態(tài)下氮趋,滑動(dòng)ViewPager2伍派,無(wú)反應(yīng)。BottomSheetBehavior可以正常響應(yīng)事件剩胁,同時(shí)诉植,事件被攔截?zé)o法傳遞到ViewPager2中。
為啥會(huì)被攔截昵观?
在onInterceptTouchEvent的返回值晾腔,有這樣一串邏輯:
View scroll = nestedScrollingChildRef != null ? nestedScrollingChildRef.get() : null;
return action == MotionEvent.ACTION_MOVE
&& scroll != null
&& !ignoreEvents
&& state != STATE_DRAGGING
&& !parent.isPointInChildBounds(scroll, (int) event.getX(), (int) event.getY())
&& viewDragHelper != null
&& Math.abs(initialY - event.getY()) > viewDragHelper.getTouchSlop();
isPointInChildBounds比較特殊,用于判斷手勢(shì)在不在"scroll"活動(dòng)范圍內(nèi)啊犬。
nestedScrollingChildRef取值邏輯
在onLayoutChild中通過(guò)findScrollingChild賦值灼擂。
View findScrollingChild(View view) {
if (ViewCompat.isNestedScrollingEnabled(view)) {
return view;
}
if (view instanceof ViewGroup) {
ViewGroup group = (ViewGroup) view;
for (int i = 0, count = group.getChildCount(); i < count; i++) {
View scrollingChild = findScrollingChild(group.getChildAt(i));
if (scrollingChild != null) {
return scrollingChild;
}
}
}
return null;
}
通過(guò)判斷isNestedScrollingEnabled來(lái)決定,并且是布局中的第一個(gè)觉至。那應(yīng)該就是布局中ViewPage2上方的RecyclerView了剔应,將其nestedScrollingEnabled設(shè)為false試試看效果,結(jié)果ViewPage2部分可以上下滑動(dòng)了,BottomSheetBehavior又不能展開(kāi)了峻贮。根據(jù)對(duì)嵌套滑動(dòng)的了解席怪,RecyclerView在響應(yīng)滑動(dòng)事件的同時(shí),也會(huì)將事件傳遞到父View中纤控,也就意味著是BottomSheetBehavior響應(yīng)的觸發(fā)邏輯出了問(wèn)題挂捻。onNestedPreScroll中有這樣一段邏輯,判斷是不是同一個(gè)view船万。
@Override
public void onNestedPreScroll(
@NonNull CoordinatorLayout coordinatorLayout,
@NonNull V child,
@NonNull View target,
int dx,
int dy,
@NonNull int[] consumed,
int type) {
...
View scrollingChild = nestedScrollingChildRef != null ? nestedScrollingChildRef.get() : null;
if (target != scrollingChild) {
return;
}
...
}
target是真正滑動(dòng)的view刻撒,即ViewPage2 item中的RecyclerView。而scrollingChild則成了ViewPager2的內(nèi)部實(shí)現(xiàn)RecyclerViewImpl唬涧。從而導(dǎo)致BottomSheetBehavior不會(huì)同步滑動(dòng)疫赎。那么就簡(jiǎn)單了,反射將RecyclerViewImpl的nestedScrollingEnabled關(guān)閉碎节,就可以修正這一問(wèn)題捧搞。
但隨之而來(lái)一個(gè)新的問(wèn)題,在切換tab的時(shí)候(也就是ViewPager2上方的RecyclerView)狮荔,仍然會(huì)失效胎撇。原因也很簡(jiǎn)單,nestedScrollingChildRef仍然是ViewPager2中前一個(gè)item中的View殖氏。
如何修復(fù)晚树?
重新觸發(fā)nestedScrollingChildRef的賦值邏輯,因在layout的過(guò)程中賦值雅采,只要requestLayout就可以達(dá)到目的爵憎。或者直接反射修改值婚瓜。
總結(jié)
將BottomSheetBehavior中所有子View的isNestedScrollingEnabled設(shè)置false宝鼓,除了需要響應(yīng)滑動(dòng)的RecyclerView。并在ViewPager2切換頁(yè)面的時(shí)候巴刻,觸發(fā)CoordinatorLayout.requestLayout()或反射修改nestedScrollingChildRef值愚铡。