Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Prevent scrolls in Bottom Sheet ancestor view

I have a CoordinatorLayout containing two NestedScrollViews, One has ScrollingViewBehavior and the other has BottomSheetBehavior and plays BottomSheet role, The problem is when I drag the BottomSheet, The first NestedScrollView scrolls too :

enter image description here

What I expect:
When I scroll BottomSheet, Another NestedScrollView should not scroll

What I tried:
I created a custom behavior and override onInterceptTouchEvent, On event that belongs to BottomSheet returned true and invoked dispatchTouchEvent on BottomSheet, It prevents the other NestedScrollView scrolls but BottomSheet can't scroll itself and click events on BottomSheet children did not work anymore.

Here's my layout :

<android.support.design.widget.CoordinatorLayout
xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
xmlns:tools="http://schemas.android.com/tools"
android:id="@+id/main_content"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:animateLayoutChanges="true"
tools:ignore="UnusedAttribute">

<include
    layout="@layout/toolbar"/>

<android.support.v4.widget.NestedScrollView
    android:id="@+id/nested"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    android:layout_marginBottom="?actionBarSize"
    android:background="#ff0"
    android:fillViewport="true"
    app:layout_behavior="@string/appbar_scrolling_view_behavior">

    <LinearLayout
        android:id="@+id/ll1"
        android:layout_width="match_parent"
        android:layout_height="match_parent"
        android:orientation="vertical"
        android:paddingTop="24dp">

        <Button
            android:id="@+id/button_1"
            android:layout_width="match_parent"
            android:layout_height="wrap_content"
            android:layout_margin="8dp"
            android:background="@android:color/holo_green_dark"
            android:padding="16dp"
            android:text="Button 1"
            android:textColor="@android:color/white"/>

        <Button
            android:id="@+id/button_2"
            android:layout_width="match_parent"
            android:layout_height="wrap_content"
            android:layout_margin="8dp"
            android:background="@android:color/holo_blue_light"
            android:padding="16dp"
            android:text="Button 2"
            android:textColor="@android:color/white"/>

        <Button
            android:id="@+id/button_3"
            android:layout_width="match_parent"
            android:layout_height="wrap_content"
            android:layout_margin="8dp"
            android:background="@android:color/holo_red_dark"
            android:padding="16dp"
            android:text="Button 3"
            android:textColor="@android:color/white"/>

        <android.support.v4.widget.Space
            android:layout_width="match_parent"
            android:layout_height="0dp"
            android:layout_weight="1"/>

        <TextView
            android:layout_width="match_parent"
            android:layout_height="wrap_content"
            android:text="Bottom..."/>

    </LinearLayout>

</android.support.v4.widget.NestedScrollView>


<android.support.v4.widget.NestedScrollView
    android:id="@+id/bottom_sheet"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    android:background="@android:color/holo_orange_light"
    android:clipToPadding="false"
    android:elevation="4dp"
    android:fillViewport="true"
    app:behavior_peekHeight="60dp"
    app:layout_behavior=".Behavior">

    <LinearLayout
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:orientation="vertical">

        <TextView
            android:id="@+id/tv"
            android:layout_width="match_parent"
            android:layout_height="wrap_content"
            android:clickable="true"
            android:elevation="6dp"
            android:focusable="true"
            android:padding="16dp"
            android:text="@string/ipsum"
            android:textSize="16sp"/>

      <RecyclerView.../>

    </LinearLayout>


</android.support.v4.widget.NestedScrollView>

and my toolbar.xml:

<?xml version="1.0" encoding="utf-8"?>
<android.support.design.widget.AppBarLayout
    xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:app="http://schemas.android.com/apk/res-auto"
    android:id="@+id/appbar"
    android:layout_width="match_parent"
    android:layout_height="320dp"
    android:fitsSystemWindows="true">

    <android.support.design.widget.CollapsingToolbarLayout
        android:layout_width="match_parent"
        android:layout_height="match_parent"
        android:fitsSystemWindows="true"
        app:layout_scrollFlags="scroll|exitUntilCollapsed">

        <ImageView
            android:layout_width="match_parent"
            android:layout_height="match_parent"
            android:scaleType="fitXY"
            android:src="@drawable/lily_lake"
            app:layout_collapseMode="parallax"/>

        <android.support.v7.widget.Toolbar
            android:id="@+id/toolbar"
            android:layout_width="match_parent"
            android:layout_height="?actionBarSize"
            android:layout_gravity="bottom"
            app:layout_collapseMode="pin"/>


    </android.support.design.widget.CollapsingToolbarLayout>

</android.support.design.widget.AppBarLayout>
like image 318
Farshad Tahmasbi Avatar asked Mar 05 '18 08:03

Farshad Tahmasbi


1 Answers

Updated with simpler solution.

The solution to your question can be divided into two sections:

  1. How to raise the bottom sheet without scrolling the appbar?
  2. How to lower the bottom sheet once raised.

To raise the bottom sheet without effected the appbar, we need to detect when the bottom sheet is responsible for the scroll and let the appbar refuse the nested scroll so it won't receive subsequent nested scroll events. This is accomplished in the onStartNestedScroll method of AppBarLayout.Behavior. (See code for MyAppBarBehavior below.)

We can now drag up the bottom sheet just like an independent sliding panel.

enter image description here

Now that we have dragged the bottom sheet up, can we drag it back down? Yes, and no. If the bottom sheet is scrolled to the top of its content, then we can drag the bottom sheet down if we touch the bottom sheet below the appbar. If we touch the bottom sheet over the appbar (which, of course, is behind the bottom sheet), then we cannot drag the bottom sheet down. This issue is present in the underlying code without our modifications. I think that it is just unusual for a bottom sheet to overlay an appbar.

To demonstrate this notice, in this video, that the appbar opens up behind the bottom sheet when the bottom sheet is dragged.

enter image description here

To change this behavior, we will override the onInterceptTouchEvent of the AppBarLayout.Behavior to return false if the bottom sheet is being touched.

Here is the new AppBarLayout.Behavior:

MyAppBarBehavior

class MyAppBarBehavior extends AppBarLayout.Behavior {
    private boolean mIsSheetTouched = false;

    @Override
    public boolean onStartNestedScroll(CoordinatorLayout coordinatorLayout, AppBarLayout child,
                                       View directTargetChild, View target, int axes, int type) {
        // Set flag if the bottom sheet is responsible for the nested scroll.
        mIsSheetTouched = target.getId() == R.id.bottom_sheet;
        // Only consider starting a nested scroll if the bottom sheet is not touched; otherwise,
        // we will let the other views do the scrolling.
        return !mIsSheetTouched
            && super.onStartNestedScroll(coordinatorLayout, child, directTargetChild,
                                         target, axes, type);
    }

    @Override
    public boolean onInterceptTouchEvent(CoordinatorLayout parent, AppBarLayout child, MotionEvent ev) {
        // Don't accept touch stream here if the bottom sheet is touched. This will permit the
        // bottom sheet to be dragged down without interaction with the appBar. Reset on cancel.
        if (ev.getActionMasked() == MotionEvent.ACTION_CANCEL) {
            mIsSheetTouched = false;
        }
        return !mIsSheetTouched && super.onInterceptTouchEvent(parent, child, ev);
    }
}

(I shouldn't have identified the bottom sheet with a specific id, but this is just a demonstration.)

Add the custom behavior to the appbar:

<android.support.design.widget.AppBarLayout 
    android:id="@+id/appbar"
    android:layout_width="match_parent"
    android:layout_height="320dp"
    android:fitsSystemWindows="true"
    app:layout_behavior=".MyAppBarBehavior"
    app:expanded="true">

Here is the final result:

enter image description here

There may be other ways to accomplish this. I would probably use an existing sliding panel library such as AndroidSlidingUpPanel instead of placing a bottom sheet in a nested scrolling environment where we must then undo the effects of the environment.

like image 66
Cheticamp Avatar answered Sep 30 '22 23:09

Cheticamp