I have a bottom sheet nested inside another bottom sheet (FrameLayouts
using the BottomSheet
layout behavior)
I also have a couple of 'peek views' (FrameLayouts
) which have click listeners attached, to expand the bottom sheet(s) respectively, when clicked.
So the app basically has 3 main screens. The 'main container', then the first 'bottom sheet', which can be expanded full-screen, and then at the bottom of the first bottom sheet, is the second bottom sheet, which can also be expanded full-screen.
Problem:
When I add a RecyclerView
to the nested bottom sheet 'container' view, dragging stops working for the second peek view (Sheet 2 Peek). If I remove the peek view ClickListener
or the RecyclerView
, things seem to work perfectly fine.
Desired result:
Both bottom sheets should remain draggable, and the peek views should be able to be clicked to expand their parent bottom sheet. The bottom sheet should respond to nested scrolls as it would normally.
I've tried removing the ClickListener
and using touch gestures instead, but nothing I've tried seems to help.
I'm using v25.3.1
of the design support library, and I'm able to reproduce this problem on a Galaxy S4 running 4.4.4 stock, and a Nexus 6P running 7.1.2 stock. (I don't have any other devices available).
I've also created a test project on github for anyone interested in taking a closer look: https://github.com/timusus/bottomsheet-test
Here's a couple of screenshots demonstrating the layout:
The layout structure looks like this (some code omitted for clarity):
<CoordinatorLayout>
<FrameLayout
android:id="@+id/mainContainer"
android:layout_height="match_parent"/>
<FrameLayout
android:id="@+id/sheet1"
android:layout_height="match_parent"
app:layout_behavior="CustomBottomSheetBehavior"
app:behavior_peekHeight="64dp">
<FrameLayout
android:id="@+id/sheet1Container"
android:layout_height="match_parent"/>
<CoordinatorLayout>
<FrameLayout
android:id="@+id/sheet2
android:layout_height="match_parent"
app:layout_behavior="CustomBottomSheetBehavior"
app:behavior_peekHeight="64dp">
<FrameLayout
android:id="@+id/sheet2Container"
android:layout_height="match_parent">
<!-- Problematic RecyclerView -->
<RecyclerView
android:layout_height="match_parent"/>
</FrameLayout>
<!-- Problematic Click Listener on this view -->
<FrameLayout
android:id="@+id/sheet2PeekView"
android:layout_height=64dp"/>
</FrameLayout>
</CoordinatorLayout>
<FrameLayout
android:id="@+id/sheet1PeekView"
android:layout_height=64dp"/>
</FrameLayout>
</CoordinatorLayout/>
The CustomBottomSheetBehavior
is just a simple subclass of BottomSheetBehavior
which prevents the first sheet from intercepting touch events if the second sheet is expanded or dragging. This allows the second sheet to be dragged from 'expanded' to 'collapsed' without also collapsing the first sheet.
public class CustomBottomSheetBehavior<V extends View> extends BottomSheetBehavior<V> {
private boolean allowDragging = true;
public void setAllowDragging(boolean allowDragging) {
this.allowDragging = allowDragging;
}
@Override
public boolean onInterceptTouchEvent(CoordinatorLayout parent, V child, MotionEvent event) {
if (!allowDragging) {
return false;
}
return super.onInterceptTouchEvent(parent, child, event);
}
}
I don't believe the customisation of BottomSheetBehavior
is relevant to this problem, but for completeness, here's how it's used:
FrameLayout sheet1 = (FrameLayout) findViewById(R.id.sheet1);
bottomSheetBehavior1 = (CustomBottomSheetBehavior) BottomSheetBehavior.from(sheet1);
FrameLayout sheet2 = (FrameLayout) findViewById(R.id.sheet2);
bottomSheetBehavior2 = (CustomBottomSheetBehavior) BottomSheetBehavior.from(sheet2);
bottomSheetBehavior2.setBottomSheetCallback(new BottomSheetBehavior.BottomSheetCallback() {
@Override
public void onStateChanged(@NonNull View bottomSheet, int newState) {
//If the second sheet is expanded or dragging, don't allow the first sheet to respond to touch events.
if (newState == BottomSheetBehavior.STATE_EXPANDED || newState == BottomSheetBehavior.STATE_DRAGGING) {
bottomSheetBehavior1.setAllowDragging(false);
} else {
bottomSheetBehavior1.setAllowDragging(true);
}
}
I can't seem to figure out if this is to do with the onInterceptTouchEvent
of the BottomSheet
, nested scroll handling of the inner RecyclerView
, View.ClickListener
stealing touch events, a combination of the above, or something else altogether.
Any help would be much appreciated.
Add this code to BottomSheetBehavior object. Dragging will be disabled. Works fine for me. This does not disable swiping.
Intercept touch events in a ViewGroup. The onInterceptTouchEvent() method is called whenever a touch event is detected on the surface of a ViewGroup , including on the surface of its children.
setCancelable(false) will prevent the bottom sheet dismiss on back press also.
just include <layout /> and attach to BottomSheetBehavior , it will work fine.
FIXED
I can't seem to figure out if this is to do with the onInterceptTouchEvent of the BottomSheet, nested scroll handling of the inner RecyclerView, View.ClickListener stealing touch events, a combination of the above, or something else altogether.
It is a combination of the above CustomBottomSheetBehavior and View.ClickListener
Issue was bottomSheetBehavior1 is taking drag event when getSheet2PeekView is dragging so detect touch event on getSheet2PeekView and set bottomSheetBehavior1 dragging false
and bottomSheetBehavior2 true
Put this code and your problem is resolved.
findViewById(getSheet2PeekViewResId()).setOnTouchListener(new OnTouchListener() {
@Override
public boolean onTouch(View v, MotionEvent event) {
Log.e(TAG, "onTouch: ");
bottomSheetBehavior1.setAllowDragging(false);
bottomSheetBehavior2.setAllowDragging(true);
return false;
}
});
Also created Pull Request to your repo with fully working changes.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With