I have a CoordinatorLayout
containing two NestedScrollView
s, 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 :
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>
Updated with simpler solution.
The solution to your question can be divided into two sections:
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.
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.
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:
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.
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