Oleksandr Berdnikov
Oleksandr Berdnikov

Reputation: 679

How to make bottom sheet (BottomSheetBehavior) expandable to arbitrary position?

I have bottom sheet, and I want to change its behavior so it would work like on the main screen of Google Maps application, where you can expand it to any position and leave it there and it won't automatically stick to the bottom or to the top. Here's my layout with bottom sheet:

<android.support.design.widget.CoordinatorLayout
    android:layout_width="match_parent"
    android:layout_height="match_parent">

    <com.google.android.gms.maps.MapView
        android:id="@+id/map"
        android:layout_width="match_parent"
        android:layout_height="match_parent"/>

    <View
        android:id="@+id/shadow"
        android:layout_width="match_parent"
        android:layout_height="16dp"
        android:background="@drawable/shape_gradient_top_shadow"
        app:layout_anchor="@+id/map_bottom_sheet" />


    <LinearLayout
        android:id="@+id/map_bottom_sheet"
        android:layout_width="match_parent"
        android:layout_height="300dp"
        android:fillViewport="false"
        android:orientation="vertical"
        app:behavior_peekHeight="50dp"
        android:background="@color/lightGray"
        app:layout_behavior="android.support.design.widget.BottomSheetBehavior">

        <include layout="@layout/bottom_sheet_top_buttons"/>
        <android.support.v4.widget.NestedScrollView
            android:id="@+id/bottom_sheet_content_fragment"
            android:layout_width="match_parent"
            android:layout_height="match_parent"
            android:background="@color/lightGray"/>

    </LinearLayout>

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

What I need in essence is eliminate forcing of STATE_EXPANDED and STATE_COLLAPSED states when dragging is ended.

Here's a visual explanation of what I try to achieve:


As you can see, bottom sheet doesn't automatically anchor to the top or the bottom but stays at whatever position it was left.

Upvotes: 9

Views: 10686

Answers (3)

Bonefish
Bonefish

Reputation: 136

I have created a proof of concept originating from the orginal source code from the design library. You can view it here. The problem with the original behavior is it doesn't allow flings, and most methods are private so extending the class and overriding some methods in an attempt to achieve it won't get you very far either. My implementation allows for optional snapping behavior, transient states (don't automatically snap after drag) and customizations around setting peek height and max height.

Upvotes: 3

mbo
mbo

Reputation: 4701

Copy the code from android.support.design.widget.BottomSheetBehavior to make your own custom behavior. Then modify the onViewReleased() method which is responsible for the movement of the sheet after the drag ends. You also have to introduce a new state besides the existing ones - the state is helpful to restore the position and let others know in which state your sheet is at the moment with getState().

@Override
public void onViewReleased(View releasedChild, float xVel, float yVel) {
    int top;
    @State int targetState;

    // Use the position where the drag ended as new top
    top = releasedChild.getTop();

    // You have to manage the states here, too (introduce a new one)
    targetState = STATE_ANCHORED;

    if (mViewDragHelper.settleCapturedViewAt(releasedChild.getLeft(), top)) {
        setStateInternal(STATE_SETTLING);
        ViewCompat.postOnAnimation(releasedChild, new SettleRunnable(releasedChild, targetState));

    } else {
        setStateInternal(targetState);
    }
}

Upvotes: 6

Alok
Alok

Reputation: 881

Hi Alex you can try this code for similar expected behaviour, it is not as optimised but it will help you to understand the concept.

     final DisplayMetrics metrics = new DisplayMetrics();
        getWindowManager().getDefaultDisplay().getMetrics(metrics);
 bottomSheetBehavior.setPeekHeight(200);
// set callback for changes
        bottomSheetBehavior.setBottomSheetCallback(new BottomSheetBehavior.BottomSheetCallback() {
            @Override
            public void onStateChanged(@NonNull View bottomSheet, int newState) {

                Log.d(TAG, "onStateChanged: " + bottomSheet.getY() + "::" + bottomSheet.getMeasuredHeight() + " :: " + bottomSheet.getTop());

            }

            @Override
            public void onSlide(@NonNull View bottomSheet, float slideOffset) {


                ViewGroup.LayoutParams params = bottomSheet.getLayoutParams();
                params.height = Math.max(0, metrics.heightPixels - (int) bottomSheet.getTop());
                bottomSheet.setLayoutParams(params);




            }
        });

Upvotes: 2

Related Questions