10

I am working on BottomSheetDialogFragment my requirement is to create Bottom menu, Where if I click outside fragment area it should not cancel the Dialog and should persist.

ISSUE: And Event outside the Fragment should propagate to the lower fragment view/fragment.

I have already tried below(doesn't work for BottomDialogFragment): Allow outside touch for DialogFragment

To stop the dialog cancel i tried Below(i call setCancelable(boolean) in onStart() of BottomDialogFragment):

@Override
    public void setCancelable(boolean cancelable) {
        super.setCancelable(cancelable);

        BottomSheetDialog dialog = (BottomSheetDialog) getDialog();
        dialog.setCanceledOnTouchOutside(cancelable);

        View bottomSheetView = dialog.getWindow().getDecorView().findViewById(R.id.design_bottom_sheet);
        BottomSheetBehavior.from(bottomSheetView).setHideable(cancelable);
    }

reference

EDIT: Found it the hard way there is no other go then using Coordinate layout.The best solution for BottomSheetDialog is here

  • This Solution solve's the issue but bring's in one more issue. i.e all the actionMode event's are not navigated while all other app event's are.
  • And this is my best solution to the problem
Anmol
  • 8,110
  • 9
  • 38
  • 63
  • Does [this link][1] help you ? [1]: https://medium.com/@betakuang/make-your-bottomsheetdialog-noncancelable-e50a070cdf07 – coroutineDispatcher Apr 16 '19 at 09:33
  • @StavroXhardha I have already tried this it allow's me to stop the cancel of Dialog but the event's are also blocked for below View's. – Anmol Apr 16 '19 at 09:52
  • I did found the solution and applied in this demo project https://github.com/andor201995/PersistIt – Anmol Nov 04 '19 at 09:46

5 Answers5

6

Try code below in your BottomSheetDialog:

 override fun onCreateDialog(savedInstanceState: Bundle?): Dialog {
     return (super.onCreateDialog(savedInstanceState) as BottomSheetDialog).apply {
         setCanceledOnTouchOutside(false)
     }
 }

or wrap by <CoordinatorLayout> for instance your <ConstraintLayout> and implement <layout /> and attach to BottomSheetBehavior.

ardiien
  • 767
  • 6
  • 26
  • Hello i tried this but when i click outside the View then the Dialog is not getting canceled but at same time all the event's are blocked which i want for lower VIew. I want to know a way to get event's to lower VIew. Thank's for your reply. – Anmol Apr 16 '19 at 09:55
  • 2
    try to use not a dialog. just include `` and attach to `BottomSheetBehavior`, it will work fine. – ardiien Apr 16 '19 at 16:51
  • For that i think i fill be forced to use coordinate layout as root so as to add BottomSheetBehavior as layout_behaviour.But i was just trying to figure out if i can use dialog and bypass the outside touch region. – Anmol Apr 16 '19 at 18:28
  • hmm, try to turn off `dim` and apply in `styles` some attributes. Maybe will be [helpful](https://stackoverflow.com/questions/29227126/how-can-i-change-default-black-dim-background-color-not-the-amount-of-dim-of) – ardiien Apr 17 '19 at 10:08
  • I think you just need to wrap by `` for instance your `` and implement `` and attach to `BottomSheetBehavior`. – ardiien Apr 17 '19 at 12:57
  • @YaroslavOvdiienko thanks for your suggestion, it works exactly what I want. thanks. – Dharmishtha Jan 01 '21 at 07:23
4

As been said by Pankaj Kumar, this is not possible by default. However, I found a workaround that works and allows touches to views outside of the bottom sheet while keeping the bottom sheet open

You can override the layout of the BottomSheetDialog like so:

values/refs.xml

<resources xmlns:tools="http://schemas.android.com/tools">
    <item name="design_bottom_sheet_dialog" type="layout" tools:override="true">@layout/custom_design_bottom_sheet_dialog</item>
</resources>

layout/custom_design_bottom_sheet_dialog.xml

<?xml version="1.0" encoding="utf-8"?>
<!--
  ~ This is an override of the design_bottom_sheet_dialog from material library
-->
<FrameLayout
    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:id="@+id/container"
    android:layout_width="match_parent"
    android:layout_height="wrap_content">

  <androidx.coordinatorlayout.widget.CoordinatorLayout
      android:id="@+id/coordinator"
      android:layout_width="match_parent"
      android:layout_height="wrap_content">

    <View
        android:id="@+id/touch_outside"
        android:layout_width="0dp"
        android:layout_height="0dp"
        android:visibility="gone"
        android:importantForAccessibility="no"
        android:soundEffectsEnabled="false"
        tools:ignore="UnusedAttribute"/>

    <FrameLayout
        android:id="@+id/design_bottom_sheet"
        style="?attr/bottomSheetStyle"
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:layout_gravity="center_horizontal|top"
        app:layout_behavior="@string/bottom_sheet_behavior"/>

  </androidx.coordinatorlayout.widget.CoordinatorLayout>

</FrameLayout>

YourCustomBottomSheetDialogFragment

override fun onStart() {
        super.onStart()

        // Set layout for custom bottom sheet by allowing background touches
        dialog?.window?.apply {
            setLayout(ViewGroup.LayoutParams.MATCH_PARENT, ViewGroup.LayoutParams.WRAP_CONTENT)
            setFlags(WindowManager.LayoutParams.FLAG_NOT_TOUCH_MODAL, WindowManager.LayoutParams.FLAG_NOT_TOUCH_MODAL)
            
            attributes = attributes.apply {
                gravity = Gravity.BOTTOM
            }
            setDimAmount(0.0f)
        }
    }

By doing this, the dialog has a wrap_content height and the flags allow touches to be handled by views outside of this dialog

Sammekl
  • 501
  • 4
  • 20
3

This is not possible till you are using BottomSheetDialogFragment. BottomSheetDialogFragment is a dialog and as behaviour of every dialog, it does not allow user interception on any view behind the dialog, although that is visible to user.

So to achieve this you need to use Fragment instead of BottomSheetDialogFragment. And yes it will require lot of code changes :) and you have to live without BottomSheetDialogFragment if you want to intercept views behind.

Pankaj Kumar
  • 81,967
  • 29
  • 167
  • 186
  • Major issue is (using Fragment) is that my app/Activity is not using `Adjust resize` but according to requirement bottom menu should move up if keyboard is displayed (Example).BottomSheetDialog is itself Intelligent to adjust according to the system resize call's.I am not able to implement same behavior in MyCustomFragment.So i am leaned to use BottomSheetDialogFragment – Anmol Apr 16 '19 at 11:08
  • 1
    I think if this is only task left then that is doable. You just need to design container layout as like to handle keyboard appears. you can look at https://code.luasoftware.com/tutorials/android/move-layout-when-keyboard-shown/ or similar threads – Pankaj Kumar Apr 16 '19 at 11:17
2

you should use android.support.design.widget.BottomSheetBehavior.

but if you want to have a bottomSheet in other class I suggest you use a Fragment and in this fragment open your bottomSheet

open your fragment in this way.

And in your fragment, open your bottomSheet in the following code:

in onInitViews

var mBottomSheetBehavior = BottomSheetBehavior.from(bottomSheetCoordinatorLayout)
mBottomSheetBehavior!!.state = BottomSheetBehavior.STATE_HIDDEN

mBottomSheetBehavior!!.setBottomSheetCallback(object : BottomSheetBehavior.BottomSheetCallback() {
     override fun onStateChanged(bottomSheet: View, newState: Int) {
          when (newState) {
             BottomSheetBehavior.STATE_HIDDEN -> {
                  fragmentManager?.popBackStack()
             }
             //BottomSheetBehavior.STATE_COLLAPSED -> "Collapsed"
             //BottomSheetBehavior.STATE_DRAGGING -> "Dragging..."
             //BottomSheetBehavior.STATE_EXPANDED -> "Expanded"
             //BottomSheetBehavior.STATE_SETTLING -> "Settling..."
           }
      }

      override fun onSlide(bottomSheet: View, slideOffset: Float) {
          //text_view_state!!.text = "Sliding..."
      }
})

And your layout should be like this:

<android.support.design.widget.CoordinatorLayout xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:app="http://schemas.android.com/apk/res-auto"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    android:layoutDirection="ltr">

    <android.support.design.widget.CoordinatorLayout
        android:id="@+id/bottomSheetCoordinatorLayout"
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        app:behavior_hideable="true"
        app:behavior_peekHeight="55dp"
        app:layout_behavior="@string/bottom_sheet_behavior">

        <RelativeLayout
            android:layout_width="match_parent"
            android:layout_height="wrap_content"
            android:background="@drawable/top_radius_primary_color"
            android:paddingStart="@dimen/material_size_32"
            android:paddingEnd="@dimen/material_size_32">

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

I hope it helps you

mohsen
  • 1,065
  • 16
  • 24
  • 1
    https://github.com/andor201995/PersistIt it has some other challenge's if you are using navigation component's.I can't use fragment transaction's.Try this demo project for further enhancement. But the scope of this question this is correct answer. – Anmol Nov 04 '19 at 09:49
0

As mentioned above,we can not directly use BottomSheetDialogFragment or BottomSheetDialog achieve the effect of allowing touch outside.

But we can realize it with the help of Dialog and BottomSheetCallback in the BottomSheetDialogFragment. (Talk is cheap,show you the code)

class BottomPanelFragment : BottomSheetDialogFragment() {

 override fun onCreateDialog(savedInstanceState: Bundle?): Dialog {
    
      val dialog = Dialog(requireActivity(), R.style.ChargeMapDialog)
        val view = requireActivity().layoutInflater.inflate(
            R.layout.fragment_around_charge_station_info,
            null
        )
        dialog.setContentView(view)

        dialog.window?.setFlags(WindowManager.LayoutParams.FLAG_NOT_TOUCH_MODAL or WindowManager.LayoutParams.FLAG_NOT_FOCUSABLE, WindowManager.LayoutParams.FLAG_NOT_TOUCH_MODAL or WindowManager.LayoutParams.FLAG_NOT_FOCUSABLE)
        dialog.window?.setFlags(WindowManager.LayoutParams.FLAG_WATCH_OUTSIDE_TOUCH, WindowManager.LayoutParams.FLAG_WATCH_OUTSIDE_TOUCH)


        dialog.window?.setDimAmount(0F)
        dialog.setCancelable(false)
        dialog.setCanceledOnTouchOutside(false)

  }   

   override fun onStart() {
        super.onStart()
        // Set layout for custom bottom sheet by allowing background touches
        dialog?.window?.apply {
            setLayout(ViewGroup.LayoutParams.MATCH_PARENT, ViewGroup.LayoutParams.WRAP_CONTENT)
            setFlags(WindowManager.LayoutParams.FLAG_NOT_TOUCH_MODAL, WindowManager.LayoutParams.FLAG_NOT_TOUCH_MODAL)

            attributes = attributes.apply {
                gravity = Gravity.BOTTOM
            }
            setDimAmount(0.0f)
        }
    }    
}


Corresponding the xml file:

 <androidx.coordinatorlayout.widget.CoordinatorLayout
        android:layout_width="match_parent"
        android:layout_height="match_parent">

    // notes:app:layout_behavior="@string/bottom_sheet_behavior"
    <androidx.constraintlayout.widget.ConstraintLayout
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:background="@color/fill_100"
        app:behavior_hideable="false"
        app:behavior_peekHeight="@dimen/dp_80"
        app:layout_behavior="@string/bottom_sheet_behavior">
        >

        <ImageView
            android:id="@+id/iv_charge_station"
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            android:layout_marginStart="@dimen/dp_24"
            android:layout_marginTop="@dimen/dp_20"
            android:src="@drawable/ic_charge_station_flag"
            app:layout_constraintStart_toStartOf="parent"
            app:layout_constraintTop_toTopOf="parent" />

        <TextView
            android:id="@+id/tv_charge_station_info"
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            android:layout_marginStart="@dimen/dp_24"
            android:layout_marginTop="@dimen/dp_8"
            android:text="Welcome driving the Tesla model-S"
            android:textColor="@color/gray_400"
            app:layout_constraintStart_toStartOf="parent"
            app:layout_constraintTop_toBottomOf="@id/iv_charge_station" />

        <TextView
            android:id="@+id/tv_select_area"
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            android:layout_marginStart="@dimen/dp_24"
            android:layout_marginEnd="@dimen/dp_20"
            android:drawablePadding="@dimen/dp_4"
            android:gravity="center"
            android:text="Los Angeles"
            android:textColor="@color/text_700"
            android:textSize="@dimen/sp_12"
            app:drawableEndCompat="@drawable/ic_arrow_down"
            app:layout_constraintBottom_toBottomOf="@id/iv_charge_station"
            app:layout_constraintEnd_toStartOf="@id/tv_select_charge_operator"
            app:layout_constraintTop_toTopOf="@id/iv_charge_station" />

        <TextView
            android:id="@+id/tv_select_charge_operator"
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            android:layout_marginLeft="16dp"
            android:layout_marginEnd="@dimen/dp_24"
            android:drawablePadding="@dimen/dp_4"
            android:gravity="center"
            android:text="select"
            android:textColor="@color/text_700"
            android:textSize="@dimen/sp_12"
            app:drawableEndCompat="@drawable/ic_arrow_down"
            app:layout_constraintBottom_toBottomOf="@id/iv_charge_station"
            app:layout_constraintEnd_toEndOf="parent"
            app:layout_constraintTop_toTopOf="@id/iv_charge_station" />


        <androidx.recyclerview.widget.RecyclerView
            android:id="@+id/rv_charge_station"
            android:layout_width="match_parent"
            android:layout_height="wrap_content"
            android:layout_marginHorizontal="@dimen/dp_24"
            android:layout_marginVertical="@dimen/dp_20"
            app:layout_constraintEnd_toEndOf="parent"
            app:layout_constraintStart_toStartOf="parent"
            app:layout_constraintBottom_toBottomOf="parent"
            app:layout_constraintTop_toBottomOf="@id/tv_charge_station_info"
            app:layoutManager="androidx.recyclerview.widget.LinearLayoutManager"
            tools:itemCount="3"
            tools:listitem="@layout/item_charge_station" />
    </androidx.constraintlayout.widget.ConstraintLayout>

    </androidx.coordinatorlayout.widget.CoordinatorLayout>

and the style.xml

 <style name="ChargeMapDialog" parent="android:Theme.Dialog">
        <item name="android:windowFrame">@null</item>
        <item name="android:windowIsFloating">true</item>
        <item name="android:windowBackground">@color/color_transparent</item>
        <item name="android:windowIsTranslucent">true</item>
        <item name="android:windowNoTitle">true</item>
        <item name="android:backgroundDimEnabled">false</item>
        <item name="android:windowCloseOnTouchOutside">false</item>
    </style>
BertKing
  • 513
  • 5
  • 13