39

How to disable BottomSheetDialogFragment dragging by finger?

I saw similar questions, but they're all about BottomSheet not BottomSheetDialogFragment.

azizbekian
  • 60,783
  • 13
  • 169
  • 249
FarshidABZ
  • 3,860
  • 4
  • 32
  • 63
  • 1
    Take a look at this explanation @Виталий Обидейко here https://stackoverflow.com/a/39892726/4394827 – Hemanth S Dec 09 '17 at 08:01

18 Answers18

41

There is simpler way of achieving the same after material design 1.2.0 was released.

https://developer.android.com/reference/com/google/android/material/bottomsheet/BottomSheetBehavior#setdraggable

When calling from BottomSheetDialogFragment:

    override fun onCreateDialog(savedInstanceState: Bundle?): Dialog {
        val bottomSheetDialog = super.onCreateDialog(savedInstanceState) as BottomSheetDialog
        bottomSheetDialog.setOnShowListener {
            val bottomSheet = bottomSheetDialog
                .findViewById<FrameLayout>(com.google.android.material.R.id.design_bottom_sheet)

            if (bottomSheet != null) {
                val behavior: BottomSheetBehavior<*> = BottomSheetBehavior.from(bottomSheet)
                behavior.isDraggable = false
            }
        }
        return bottomSheetDialog
    }

Or with styling:

    <style name="SomeStyle" parent="Theme.MaterialComponents.Light.BottomSheetDialog">
        <item name="behavior_draggable">false</item>
    </style>

And then in onCreate of your dialog fragment:

    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)
        setStyle(DialogFragment.STYLE_NORMAL, R.style.SomeStyle)
    }
jakubbialkowski
  • 1,546
  • 16
  • 24
32

Having created MyActivity as follows:

public class MyActivity extends AppCompatActivity {

    @Override
    protected void onCreate(@Nullable Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_my);

        new MyBottomSheetFragment().show(getSupportFragmentManager(), "tag");
    }

    public static class MyBottomSheetFragment extends BottomSheetDialogFragment {

        @Override
        public void setupDialog(Dialog dialog, int style) {
            BottomSheetDialog bottomSheetDialog = (BottomSheetDialog) dialog;
            bottomSheetDialog.setContentView(R.layout.sample);

            try {
                Field behaviorField = bottomSheetDialog.getClass().getDeclaredField("behavior");
                behaviorField.setAccessible(true);
                final BottomSheetBehavior behavior = (BottomSheetBehavior) behaviorField.get(bottomSheetDialog);
                behavior.setBottomSheetCallback(new BottomSheetBehavior.BottomSheetCallback() {

                    @Override
                    public void onStateChanged(@NonNull View bottomSheet, int newState) {
                        if (newState == BottomSheetBehavior.STATE_DRAGGING{ 
                            behavior.setState(BottomSheetBehavior.STATE_EXPANDED);
                        }
                    }

                    @Override
                    public void onSlide(@NonNull View bottomSheet, float slideOffset) {
                    }
                });
            } catch (NoSuchFieldException e) {
                e.printStackTrace();
            } catch (IllegalAccessException e) {
                e.printStackTrace();
            }
        }
    }
}

Where R.layout.sample is a simple layout:

<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
    android:layout_width="match_parent"
    android:layout_height="wrap_content"
    android:orientation="vertical">

    <View
        android:layout_width="match_parent"
        android:layout_height="100dp"
        android:background="#e479da" />

    <View
        android:layout_width="match_parent"
        android:layout_height="100dp"
        android:background="#798de4" />

    <View
        android:layout_width="match_parent"
        android:layout_height="100dp"
        android:background="#e4db79" />

</LinearLayout>

You'll get following output:

A part of solution is borrowed from this answer.

Tamoxin
  • 436
  • 5
  • 11
azizbekian
  • 60,783
  • 13
  • 169
  • 249
  • 2
    This is good sample code, but there are some issues. First, setupDialog isn't the BottomSheetDialogFragment method. Second, I have a recycler view in my bottom sheet view, and when I drag bottom sheet from recyclerView's blank spaces bottom sheet slides down. – FarshidABZ Dec 03 '17 at 13:52
  • [`setupDialog()`](https://android.googlesource.com/platform/frameworks/support/+/refs/heads/master/fragment/src/main/java/android/support/v4/app/DialogFragment.java#324) is a method from `DialogFragment` class. Have substituted it with `onCreateDialog()`. Cannot understand your use case. Can you post a simple project at github with that behavior? – azizbekian Dec 03 '17 at 14:39
  • it's getting warning in my fragment: java.lang.NoSuchFieldException: No field mBehavior in class Landroid/support/design/widget/BottomSheetDialog; (declaration of 'android.support.design.widget.BottomSheetDialog' ) System.err: at java.lang.Class.getDeclaredField(Native Method) – axita.savani May 06 '19 at 11:42
  • 1
    The field's name is behavior, no mBehavior. That's why you are getting that error. – Tamoxin May 31 '19 at 01:12
  • Thanks, @MarcoCarrizales, updated answer from `mBehavior` to `behavior`. – azizbekian May 31 '19 at 06:51
  • I also changed the code to be in setupDialog() instead of onCreateDialog() – Tamoxin May 31 '19 at 16:55
  • @sagarsuri I just posted the Kotlin version with data binding of this answer. I hope you find it useful. – Tamoxin Jun 05 '19 at 16:31
  • Answer by @jakubbialkowski below is better in my opinion. – Bugs Happen Jun 15 '21 at 09:08
17

Too late but worth to share.

  behavior.setDraggable(false)

This line did the job.

abby
  • 350
  • 4
  • 21
Devendra Singh
  • 2,343
  • 4
  • 26
  • 47
  • what class has isDraggable? – Andrey Uglev Mar 05 '20 at 11:00
  • `final BottomSheetBehavior behavior = BottomSheetBehavior.from(bottomSheet);` https://stackoverflow.com/questions/35618998/how-to-implement-bottom-sheets-using-new-design-support-library-23-2/35620244 – Devendra Singh Mar 05 '20 at 11:23
  • @AndreyUglev it comes from `BottomSheetDialog.behavior`. – lasec0203 Nov 09 '20 at 23:51
  • This is the correct answer. This method was added in a new version of the material library. I am using `com.google.android.material:material:1.3.0` and I have the `setDraggable()` method. I did not have the method when I was on version `1.1.0`. It is in the documentation as well... https://developer.android.com/reference/com/google/android/material/bottomsheet/BottomSheetBehavior#setDraggable(boolean) – Stephen Ruda Jun 29 '21 at 21:14
11
override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
        super.onViewCreated(view, savedInstanceState)
    //Disable dragging by set isDraggable to false
    val bottomSheetDialog = dialog as BottomSheetDialog
    val bottomSheetBehavior = bottomSheetDialog.behavior
    bottomSheetBehavior.isDraggable = false
}
Ticherhaz FreePalestine
  • 2,738
  • 4
  • 20
  • 46
Saka
  • 121
  • 1
  • 7
9

If you want to disable BottomSheetDialog dragging, try to set setCancelable(false).

Ivan Platonov
  • 145
  • 1
  • 1
7

My version. It works perfectly.

Edit 09/04/2020: Replaced depreciated setBottomSheetCallback() with addBottomSheetCallback()

class FragMenuBDrawer : BottomSheetDialogFragment() {

    ...

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

        dialog.setOnShowListener {
            val bottomSheet = (it as BottomSheetDialog).findViewById<View>(com.google.android.material.R.id.design_bottom_sheet) as FrameLayout?
            val behavior = BottomSheetBehavior.from(bottomSheet!!)
            behavior.state = BottomSheetBehavior.STATE_EXPANDED

            behavior.addBottomSheetCallback(object : BottomSheetBehavior.BottomSheetCallback() {
                override fun onStateChanged(bottomSheet: View, newState: Int) {
                    if (newState == BottomSheetBehavior.STATE_DRAGGING) {
                        behavior.state = BottomSheetBehavior.STATE_EXPANDED
                    }
                }

                override fun onSlide(bottomSheet: View, slideOffset: Float) {}
            })
        }

        // Do something with your dialog like setContentView() or whatever
        return dialog
    }

    ...
}
Sattar Hummatli
  • 1,360
  • 1
  • 15
  • 26
5

In your oncreateView

//Kotlin
val baseDialog = dialog
if (baseDialog is BottomSheetDialog) {
    baseDialog.behavior.isDraggable = false
}
//If cancelable also not required.
isCancelable = false
kiner_shah
  • 3,939
  • 7
  • 23
  • 37
3

This is the Kotlin version of azizbekian's answer since someone asked about using data binding

@SuppressLint("RestrictedApi")
override fun setupDialog(d: Dialog?, style: Int) {
    super.setupDialog(d, style)
    dialogExampleBinding = DataBindingUtil
        .inflate(LayoutInflater.from(context), R.layout.dialogExample, null, false) //This is for data binding only
    d?.setContentView(R.layout.dialogExample)

    val myDialog:BottomSheetDialog = d as BottomSheetDialog
    val dField = myDialog.javaClass.getDeclaredField("behavior") //This is the correct name of the variable in the BottomSheetDialog class
    dField.isAccessible = true
    val behavior = dField.get(d) as BottomSheetBehavior<*>
    behavior.setBottomSheetCallback(object : BottomSheetBehavior.BottomSheetCallback() {
        override fun onStateChanged(bottomSheet: View, newState: Int) {
            if (newState == BottomSheetBehavior.STATE_DRAGGING) {
                behavior.state = BottomSheetBehavior.STATE_EXPANDED
            }
        }

        override fun onSlide(bottomSheet: View, slideOffset: Float) {}
    })
}
Tamoxin
  • 436
  • 5
  • 11
2

Just Add bottomSheetBehavior.setHideable(false);

You can get Object of BottomSheetBehaviour in BottomSheetDialogFragment.

 CoordinatorLayout.LayoutParams params = (CoordinatorLayout.LayoutParams) ((View) view.getParent()).getLayoutParams();
            CoordinatorLayout.Behavior behavior = params.getBehavior();
            View parent = (View) view.getParent();
            getHeight(view);
            ((BottomSheetBehavior) behavior).setFitToContents(true);
            BottomSheetBehavior bottomSheetBehavior = BottomSheetBehavior.from(parent);
bottomSheetBehavior.setHideable(false);
Ryan M
  • 18,333
  • 31
  • 67
  • 74
Dilavar Malek
  • 1,157
  • 11
  • 18
2

This works for me sir

override fun onCreateDialog(savedInstanceState : Bundle?) : Dialog {
    val dialog = super.onCreateDialog(savedInstanceState);
    dialog.setOnShowListener {
        val bottomSheetDialog : BottomSheetDialog = it as BottomSheetDialog;
        var bottomSheetBehavior = BottomSheetBehavior<FrameLayout>();
        bottomSheetBehavior = bottomSheetDialog.getBehavior()
        bottomSheetBehavior.setDraggable(false);
    }
    return dialog }
Ferer Atlus
  • 256
  • 2
  • 6
1

This is how I managed to fix it:

mBehavior = BottomSheetBehavior.from((View) rootView.getParent());
mBehavior.setHideable(false);
Mr T
  • 1,409
  • 1
  • 17
  • 24
1

Top rated answer contains boilerplate code such as Field and try-catches.

So here is a better version of it in Kotlin:

override fun onCreateDialog(savedInstanceState: Bundle?): Dialog {
    return super.onCreateDialog(savedInstanceState).apply {
        setOnShowListener(::onShow)
    }
}

private fun onShow(dialogInterface: DialogInterface) {
    val dialog = dialogInterface as BottomSheetDialog
    val frameLayout =
        dialog.findViewById<FrameLayout>(com.google.android.material.R.id.design_bottom_sheet)
            ?: return

    BottomSheetBehavior.from(frameLayout).apply {
        addBottomSheetCallback(object : BottomSheetBehavior.BottomSheetCallback() {
            override fun onStateChanged(bottomSheet: View, newState: Int) {
                if (newState == BottomSheetBehavior.STATE_DRAGGING)
                    state = BottomSheetBehavior.STATE_EXPANDED
            }

            override fun onSlide(bottomSheet: View, slideOffset: Float) = Unit
        })
    }
}

Use it in BottomSheetDialogFragment

Akbolat SSS
  • 1,761
  • 15
  • 23
0

I get the answer here, I just added content.setLayoutParams(new CoordinatorLayout.LayoutParams(CoordinatorLayout.LayoutParams.MATCH_PARENT, CoordinatorLayout.LayoutParams.MATCH_PARENT)); to make Bottom Sheet Dialog Fragment height is match_parent and make it soft when it showed.

@NonNull
@Override
public Dialog onCreateDialog(Bundle savedInstanceState) {
    final Dialog d = super.onCreateDialog(savedInstanceState);
    // view hierarchy is inflated after dialog is shown
    d.setOnShowListener(new DialogInterface.OnShowListener() {
        @Override
        public void onShow(DialogInterface dialogInterface) {
            //this disables outside touch
            d.getWindow().findViewById(R.id.touch_outside).setOnClickListener(null);
            //this prevents dragging behavior
            View content = d.getWindow().findViewById(R.id.design_bottom_sheet);
            content.setLayoutParams(new CoordinatorLayout.LayoutParams(CoordinatorLayout.LayoutParams.MATCH_PARENT, CoordinatorLayout.LayoutParams.MATCH_PARENT));
            ((CoordinatorLayout.LayoutParams) content.getLayoutParams()).setBehavior(null);
        }
    });
    return d;
}
Miftakhul Arzak
  • 1,166
  • 1
  • 12
  • 31
0

Simple solution

    CoordinatorLayout.Behavior<View> behavior;

    View profile_config_layout_bottom_sheet = findViewById(R.id.list_view_audience_profile_config_layout);

    CoordinatorLayout.LayoutParams layoutParams = (CoordinatorLayout.LayoutParams) profile_config_layout_bottom_sheet.getLayoutParams();
    behavior = layoutParams.getBehavior();
    assert behavior != null;
    ((BottomSheetBehavior<View>) behavior).addBottomSheetCallback(new BottomSheetBehavior.BottomSheetCallback() {
        @Override
        public void onStateChanged(@NonNull View bottomSheet, int newState) {
            if (newState == BottomSheetBehavior.STATE_DRAGGING) {
                ((BottomSheetBehavior<View>) behavior).setState(BottomSheetBehavior.STATE_EXPANDED);
            } 
        }
        @Override
        public void onSlide(@NonNull View bottomSheet, float slideOffset) {}
    });
ankalagba
  • 94
  • 1
  • 8
0

This is my solution:

setOnShowListener {
            Handler().post {
                val bottomSheet = findViewById<View>(R.id.design_bottom_sheet) as? FrameLayout
                bottomSheet?.let {
                    BottomSheetBehavior.from(it).state = STATE_EXPANDED

                    // Disable dialog dragging behavior which causes issue on EditText scroll!
                    BottomSheetBehavior.from(it).isDraggable = false
                }

            }
        }
Osama Remlawi
  • 2,356
  • 20
  • 21
0

my simple solution:

 override fun onCreateDialog(savedInstanceState: Bundle?): Dialog {
    val dialog = BottomSheetDialog(requireContext(), R.style.DialogRoundedCornerStyle)
    dialog.behavior.apply {
        state = BottomSheetBehavior.STATE_EXPANDED
        isDraggable = false
    }
    return dialog
}
Романыч
  • 247
  • 7
  • 13
0

They can also do this by setting up draggable, so here's another example of mine.

My BottomSheetDialogFragment contains a RecyclerView inside, if you need a BottomSheetDialogFragment banned drag up, can drag down, What I did is I set the RecyclerView to an absolute height

 <androidx.recyclerview.widget.RecyclerView
        android:layout_width="match_parent"
        android:layout_height="367dp"/>
midFang
  • 96
  • 3
0

If you want to just disable the dismissing of bottomsheet, you can simply set isCancelable false to fragment instance

val bSheet = BottomSheetFragment.newInstance("") bSheet.isCancelable = false bSheet.show(supportFragmentManager, "sheet")