Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Android: Nested bottom sheet click/drag touch event issue

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:

123

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.

like image 927
Tim Malseed Avatar asked May 02 '17 15:05

Tim Malseed


People also ask

How do I stop my android from dragging the bottom sheet?

Add this code to BottomSheetBehavior object. Dragging will be disabled. Works fine for me. This does not disable swiping.

How to Intercept touch event android?

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.

How do I make my bottom sheet not dismissable?

setCancelable(false) will prevent the bottom sheet dismiss on back press also.

How do I allow outside touch for Bottomsheetdialog?

just include <layout /> and attach to BottomSheetBehavior , it will work fine.


Video Answer


1 Answers

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


Solution

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.

like image 174
N J Avatar answered Oct 23 '22 16:10

N J