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

I have a bottom sheet and I want to change its behavior so that it works like on the main screen of the Google Maps app, where you can expand it to any position and leave it there and it won't automatically stick to the bottom or 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>

      

I need to basically eliminate forcing the STATE_EXPANDED and STATE_COLLAPSED states when the drag is complete.

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

BxFIce8.gif
As you can see, the bottom sheet does not automatically snap to the top or bottom, but remains in whatever position it was left in.

+3


source to share


3 answers


Hi Alex, you can try this code for similar expected behavior, it is not so optimized but will help you 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);




            }
        });

      

+2


source


Copy the code from android.support.design.widget.BottomSheetBehavior

to create your own behavior. Then change the method onViewReleased()

that is responsible for moving the sheet after you finish dragging. You also have to introduce a new state, in addition to the existing ones - the state helps to restore the position and tell others what state your sheet is in at the moment with the help 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);
    }
}

      

+2


source


I created a proof of concept coming from source code from the design library. You can view it here . The problem with the original behavior is that it doesn't allow flings and most of the methods are private, so extending the class and overriding some methods in an attempt to achieve this won't get you very far either. My implementation allows for additional snapping behavior, transient states (not automatically snapping after dragging), and adjustments when setting the gap height and maximum height.

+1


source







All Articles