Make your BottomSheetDialog noncancelable
Update: I wrote a standalone library for solving this issue. You can check it out at https://github.com/beta/android-lockable-bottom-sheet.
Android Design Support Library 23.2 comes with the support for bottom sheets of Material Design, and developers can now create a materialized bottom sheet easily with the help of BottomSheetDialog or BottomSheetDialogFragment. Problem comes: you cannot make a persistent bottom sheet by using the
DialogFragment#setCancelable method, so that your bottom sheet dialog will always be canceled or dismissed no matter when the user clicks outside the dialog or when a swipe-down gesture is performed. But there are times when we need to make it stuck there to let the user wait before he/she can dismiss it or perform other actions. Here’s a workaround to make your BottomSheetDialog noncancelable.
Let’s find out what makes our bottom sheets canceled first. As mentioned above there are two ways to cancel a BottomSheetDialog, by clicking outside the dialog or by swiping down on the dialog. A normal Dialog could be set persistent with
setCancelable(false), but a BottomSheetDialog could simply not, as it’s implemented in a different approach. You can find a layout resource named design_bottom_sheet_dialog in the package of design library, and there is a View component with id set to
touch_outside, which is apparently used for detecting clicks outside the dialog.
And there is an OnClickListener set in method
wrapInBottomSheet of BottomSheetDialog.
That’s the first way of dismissing your bottom sheet dialog. It’s easy to remove the OnClickListener of view
Now when you click outside the sheet, it will not be dismissed anymore. However when you swipe down on the sheet, it will also be canceled. You may have noticed the name of the method I’ve just mentioned, which is wrapInBottomSheet. What is wrapped in a bottom sheet? Where’s the bottom sheet? Well, there’s a FrameLayout named
design_bottom_sheet in the layout XML file, and the
layout_behavior attribute is set to
bottom_sheet_behavior, which is associated with class BottomSheetBehavior bundled in the design library.
The BottomSheetBehavior handles all the touch events of the bottom sheet dialog, and switches the dialog to different state according to the current position and moving direction of the dialog. It provides an abstract class BottomSheetCallback which acts like a listener for the state changing and sliding events. You can find a callback instance at the end of class BottomSheetDialog, which is used to dismiss the dialog when there is a swipe-down gesture performed and the state of the dialog is set to
Luckily, neither do we need to overwrite the callback instance, nor we has to try to remove the callback. BottomSheetBehavior provides a convenient method for us, which is
BottomSheetBehavior#setHideable. To call this method you need to first pass a View object with the
bottom_sheet_behavior attribute using method
setHideable(false), you cannot swipe down to dismiss the bottom sheet when it at the collapsed state. However you can still switch it between the collapsed state and the expanded state.
Time to write a new
setCancelable for your dialog (the code snippet below is used in a derived class of BottomSheetDialogFragment instead of BottomSheetDialog).