Search code examples
androidandroid-coordinatorlayoutbehavior

BottomSheetDialog get Behavour always returns null


I working with BottomSheetDialog and i have to get Behavior so can set setBottomSheetCallback() to handle some stuff.

As google says i had to put Coordinator on parentView and add behavior to it. I defined CoordinatorLayout in MainActivity (root activity) 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"
    xmlns:tools="http://schemas.android.com/tools"
    android:tag="coordinatorLayout"
    app:layout_behavior="android.support.design.widget.BottomSheetBehavior"

...

This is try to get from activity:

 public void setupDialog(final Dialog dialog, int style) {

 CoordinatorLayout coordinatorLayout = getActivity().getWindow().getDecorView();
 BottomSheetBehavior behavior = BottomSheetBehavior.from(coordinatorLayout);

I also tried:

CoordinatorLayout coordinatorLayout = getActivity().getWindow().getDecorView().findViewById(R.id.coordinatorLayout); 
//this is point to the coordinatorView 

BottomSheetBehavior behavior = BottomSheetBehavior.from(coordinatorLayout);
//But this returns same error that "The view is not a child of CoordinatorLayout"

As you see i passed the coordinator-layout but method can not find behavior in it. I also should mention to points in using BottonSheetDialog:

  1. I show my BottonSheetFragments like this:
  2. I inflated my BottomSheetDialog in OnCreateView (not in setupDialog()) for ability of adding View Pager inside. As you may know ViewPager wont attach to BottonSheetDialog if you inflate view in onSetupDialog().

Any way i could not get behavior of CoordinatorLayout of parent. In my bottonSheetDialog i try these methods and non of them works and i get "The view is not a child of CoordinatorLayout" error.

code for point 1:

MyFragment myFragment= MyFragment.getInstance(bundle);
myFragment.show(fragment.getChildFragmentManager(),"tag");

code for point 2:

@Override
public View onCreateView(@NonNull LayoutInflater inflater, @Nullable ViewGroup container,@Nullable Bundle savedInstanceState) {
rootView = inflater.inflate(R.layout.fragment_bottomsheet, null, false);  
return rootView;
}

Solution

  • BottomSheetDialog is a rather peculiar Dialog implementation. It is not added to, nor does it rely on*, a CoordinatorLayout in your Activity's layout. It sets up its own CoordinatorLayout internally, and within that, a FrameLayout with BottomSheetBehavior, into which your View is placed. The BottomSheetDialog itself fills the whole screen, and has a transparent background, so that it can handle the bottom sheet interaction, and any outside touches.

    If you need access to that bottom sheet and its BottomSheetBehavior, we'll need to get it from the Dialog's View hierarchy. That's as simple as calling findViewById(R.id.design_bottom_sheet) on the Dialog, but we'll need to wait until the Dialog is shown to modify the BottomSheetBehavior. Furthermore, since BottomSheetDialog sets its own BottomSheetCallback, we must ensure that we replace it appropriately. That is, we must take care of cancelling the Dialog when it hits the closed state. For example:

    final BottomSheetDialog bsd = new BottomSheetDialog(MainActivity.this);
    bsd.setContentView(R.layout.your_dialog_layout);
    bsd.show();
    
    FrameLayout bottomSheet = (FrameLayout) bsd.findViewById(R.id.design_bottom_sheet);
    BottomSheetBehavior behavior = BottomSheetBehavior.from(bottomSheet);
    behavior.setBottomSheetCallback(new BottomSheetBehavior.BottomSheetCallback() {
            @Override
            public void onStateChanged(View bottomSheet, int newState) {
                // This is the crucial bit.
                if (newState == BottomSheetBehavior.STATE_HIDDEN) {
                    bsd.cancel();
                }
            }
    
            @Override
            public void onSlide(View bottomSheet, float slideOffset) {}
        }
    );
    

    If you're using a BottomSheetDialogFragment, the Dialog is shown in DialogFragment's onStart(), and we can override that method to do our modifications there, after the super call. For example:

    public class MyFragment extends BottomSheetDialogFragment {
        public MyFragment() {}
    
        @Override
        public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) {
            return inflater.inflate(R.layout.your_dialog_layout, container, false);
        }
    
        @Override
        public void onStart() {
            super.onStart();
    
            FrameLayout bottomSheet = getDialog().findViewById(R.id.design_bottom_sheet);
            BottomSheetBehavior behavior = BottomSheetBehavior.from(bottomSheet);
            behavior.setBottomSheetCallback(new BottomSheetBehavior.BottomSheetCallback() {
                    @Override
                    public void onStateChanged(View bottomSheet, int newState) {
                        // This is the crucial bit.
                        if (newState == BottomSheetBehavior.STATE_HIDDEN) {
                            getDialog().cancel();
                        }
                    }
    
                    @Override
                    public void onSlide(View bottomSheet, float slideOffset) {}
                }
            );
        }
    }
    

    In either case, you can do pretty much whatever you want in the BottomSheetCallback, as long as you cancel() the Dialog in onStateChanged() when newState == BottomSheetBehavior.STATE_HIDDEN.


    *Incidentally, this means that you do not have to have a CoordinatorLayout in your Activity'a layout to use BottomSheetDialog or BottomSheetDialogFragment, though I'm not sure that's made clear anywhere in the documentation or other developer resources.