preface

In the past, if you wanted a Dialog box to pop up at the bottom, you would inherit the Dialog, set the layout, set the position, set the height and width, and if you wanted to add a bottom-to-top animation, you would need to create a new animation file, but later, We have the official offer BottomSheetDialogFragment, BottomSheetDialog, bottomSheetBehavior these classes, a few lines of code can achieve the effect.

As shown below, you can simply create a Dialog that pops up at the bottom.

var bottomSheetDialog = BottomSheetDialog(this)
bottomSheetDialog.setContentView(R.layout.dialog)
bottomSheetDialog.show()
Copy the code

You can also use BottomSheetDialogFragment, BottomSheetDialogFragment inherited from DialogFragment, we can rewrite the onCreateDialog method when needed, returns the custom Dialog, He also returns BottomSheetDialog by default,

public class BottomSheetDialogFragment extends AppCompatDialogFragment {
  private boolean waitingForDismissAllowingStateLoss;
  @NonNull
  @Override
  public Dialog onCreateDialog(@Nullable Bundle savedInstanceState) {
    return newBottomSheetDialog(getContext(), getTheme()); }... }Copy the code

Drag and drop

If the view has a lot of things to display, the default is to show only one part of the view and drag and drop the other part up to display. To see how this works, you can continue down. In this case, just create a new dialog layout with the height set to match_parent.

class MemberBottomSheetDialog : BottomSheetDialogFragment(a){
    override fun onCreateView( inflater: LayoutInflater, container: ViewGroup? , savedInstanceState: Bundle? ): View? = inflater.inflate(R.layout.dialog, container, false)
}

MemberBottomSheetDialog().show(supportFragmentManager,"TAG")
Copy the code


      
<RelativeLayout 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:layout_width="match_parent"
    android:background="#ffffff"
    android:layout_height="match_parent">

    <FrameLayout
        android:layout_width="match_parent"
        android:layout_height="match_parent">

    </FrameLayout>
</RelativeLayout>

Copy the code

But you’ll notice that the content is only displayed when the root layout is RelativeLayout.

Modify the height

Due to BottomSheetDialogFragment use BottomSheetDialog, he is the default height calculation parentHeight – parentWidth * 9/16, but his foreign provides a method to us, To set the initial state, as shown below:

override fun onCreateDialog(savedInstanceState: Bundle?): Dialog {
    varbottomSheetDialog = BottomSheetDialog(context!!) ; bottomSheetDialog.behavior.peekHeight=100;
    return bottomSheetDialog
}
Copy the code

Open by default

Another problem is that the default is not, if you want to, also is the full screen, you can set the state for BottomSheetBehavior. STATE_EXPANDED.

override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
    super.onViewCreated(view, savedInstanceState)
    
    if (dialog is BottomSheetDialog) {
        val behaviour = (dialog as BottomSheetDialog).behavior behaviour.state = BottomSheetBehavior.STATE_EXPANDED }
}
Copy the code

Ban on drag and drop

SetDraggable sets whether draggable can be collapsed/expanded by dragging. When dragging is disabled, the application needs to implement a custom way to expand/collapse the dialog box.

override fun onCreateDialog(savedInstanceState: Bundle?): Dialog {
    val bottomSheetDialog = super.onCreateDialog(savedInstanceState) as BottomSheetDialog

    bottomSheetDialog.behavior.isDraggable=false

    return bottomSheetDialog
}
Copy the code

The background remains dark

By default, when the dialog box pops up, it will be dark, but you can just add a style.

<style name="myDialog" parent="Theme.MaterialComponents.BottomSheetDialog">
    <item name="android:backgroundDimEnabled">false</item>
</style>
Copy the code
override fun onCreateDialog(savedInstanceState: Bundle?): Dialog {
    returnBottomSheetDialog(context!! , R.style.myDialog) }Copy the code

Monitor the scroll

Sometimes need to drag up time to do some linkage, you need to get the value of the sliding dialog by behaviors. AddBottomSheetCallback.

SlideOffset is between 0 and 1, 0 by default, 1 when it slides to the top, -1 when it disappears,

override fun onCreateDialog(savedInstanceState: Bundle?): Dialog {
    val bottomSheetDialog = super.onCreateDialog(savedInstanceState) as BottomSheetDialog
    bottomSheetDialog.behavior.addBottomSheetCallback(object :
        BottomSheetBehavior.BottomSheetCallback() {
        override fun onStateChanged(bottomSheet: View, newState: Int) {
            Log.i(TAG, "onStateChanged: ")}override fun onSlide(bottomSheet: View, slideOffset: Float) {
            Log.i(TAG, "onSlide: ${slideOffset}")}})return bottomSheetDialog
}
Copy the code

How do you calculate when you drag up?

This problem is very simple, it does internal drag using ViewDragHelper, so when we can’t drag up, there are two ways to use ViewDragHelper, one is to return false in tryCaptureView, 2 it is in the clampViewPositionVertical returns a value, the value returned to one of the biggest drag, when we drag and drop to the top, will not be able to continue upward.

The default height is parentHeight – parentWidth * 9/16, but if our View is smaller than this value, then the final height is the smallest, that is, the value of the View, so that the dialog box is already displayed. Why do we have to move it up?

But if our View height is greater than this value, it will take parentHeight – parentWidth * 9/16 as the height of the Dialog. How much space can we move up? The answer is View height -parentHeight – parentWidth * 9/16.

For example, on my phone, parentHeight is 2159, so if View is 1600, then I can move it up by 40px by defining the fitToContentsOffset variable, which is parentHeight childHeight. The free space of the screen, when dragging up trigger clampViewPositionVertical method, he returned to the value of the vertical position, as a new so when drag and drop to the top, will return fitToContentsOffset.

BottomSheetBehavior implements the bottom Dialog

BottomSheetDialog is also implemented using BottomSheetBehavior. First, you define a layout. Note that the root layout can only be CoordinatorLayout.


      
<androidx.coordinatorlayout.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:layout_width="match_parent"
    android:layout_height="match_parent"
    tools:context=".MainActivity">

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

        <Button
            android:id="@+id/bt"
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            android:text="Open" />
    </LinearLayout>

    <LinearLayout
        android:background="@color/cardview_dark_background"
        android:id="@+id/design_bottom_sheet1"
        android:layout_width="match_parent"
        android:layout_height="match_parent"
        android:orientation="vertical"
        app:behavior_hideable="true"
        app:behavior_peekHeight="300dp"
        app:elevation="6dp"
        app:layout_behavior="@string/bottom_sheet_behavior"></LinearLayout>

</androidx.coordinatorlayout.widget.CoordinatorLayout>
Copy the code
  var bottomSheetBehavior = BottomSheetBehavior.from(findViewById(R.id.design_bottom_sheet1));
 bottomSheetBehavior.setState(BottomSheetBehavior.STATE_HIDDEN);
 findViewById<View>(R.id.bt).setOnClickListener {
     if (bottomSheetBehavior.state == BottomSheetBehavior.STATE_HIDDEN) {
         bottomSheetBehavior.setState(BottomSheetBehavior.STATE_COLLAPSED)
     } else if (bottomSheetBehavior.state == BottomSheetBehavior.STATE_COLLAPSED) {
         bottomSheetBehavior.state = BottomSheetBehavior.STATE_HIDDEN
     }
 }
Copy the code