I have implemented bottomsheet behavior with NestedScrollView. And was wondering if it is possible to hide the bottomsheet view when touched outside.
- 
                    2Is it a Self answered question? Because you have added question and answer same time, so I was asking. – Pankaj Kumar Jul 04 '16 at 13:27
 - 
                    6Yup so that others don't feel frustrated if having same problem. – Pradeep Kumar Kushwaha Jul 04 '16 at 13:30
 - 
                    For `BottomSheetDialogFragment` see https://stackoverflow.com/questions/40616833/bottomsheetdialogfragment-listen-to-dismissed-by-user-event. – CoolMind Apr 17 '19 at 08:42
 
10 Answers
Finally I was able to do this,
Used the following lines of code:
@Override public boolean dispatchTouchEvent(MotionEvent event){
    if (event.getAction() == MotionEvent.ACTION_DOWN) {
        if (mBottomSheetBehavior.getState()==BottomSheetBehavior.STATE_EXPANDED) {
            Rect outRect = new Rect();
            bottomSheet.getGlobalVisibleRect(outRect);
            if(!outRect.contains((int)event.getRawX(), (int)event.getRawY()))
                mBottomSheetBehavior.setState(BottomSheetBehavior.STATE_COLLAPSED);
        }
    }
    return super.dispatchTouchEvent(event);
}
Hope it save someone's whole day!
- 2,231
 - 3
 - 23
 - 34
 
- 
                    1
 - 
                    9
 - 
                    1@RajeshNasit Implement the DispatchTouchEvent method in your activity and use the FragmentManager to call the Method in your Fragment. – Luca Ziegler Jan 13 '18 at 16:14
 - 
                    I done this but problem is that touch event called on click of swipe of each view. I want it for only specific views swipe – Rajesh N Jan 13 '18 at 16:16
 - 
                    
 - 
                    1It works but is that a good solution ? Because each time we touch or scroll the screen, that method `dispatchTouchEvent` is called serveral times wich may be bad for battery usage and application performance. – ankalagba Jun 05 '21 at 20:40
 
Thanks the OP for the question/answer. I've used his code but improved on its cleanliness and wanted to share. Instead of extending a View and adding the interface, you can code that directly in the BottomSheetBehavior. Like this:
AutoCloseBottomSheetBehavior.java
import android.content.Context;
import android.graphics.Rect;
import android.support.design.widget.BottomSheetBehavior;
import android.support.design.widget.CoordinatorLayout;
import android.util.AttributeSet;
import android.view.MotionEvent;
import android.view.View;
public class AutoCloseBottomSheetBehavior<V extends View> extends BottomSheetBehavior<V> {
    public AutoCloseBottomSheetBehavior(Context context, AttributeSet attrs) {
        super(context, attrs);
    }
    @Override
    public boolean onInterceptTouchEvent(CoordinatorLayout parent, V child, MotionEvent event) {
        if (event.getAction() == MotionEvent.ACTION_DOWN &&
            getState() == BottomSheetBehavior.STATE_EXPANDED) {
            Rect outRect = new Rect();
            child.getGlobalVisibleRect(outRect);
            if (!outRect.contains((int) event.getRawX(), (int) event.getRawY())) {
                setState(BottomSheetBehavior.STATE_COLLAPSED);
            }
        }
        return super.onInterceptTouchEvent(parent, child, event);
    }
}
and then you simply add it to your XML layout:
<?xml version="1.0" encoding="utf-8"?>
<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:layout_width="match_parent"
    android:layout_height="match_parent">
   ... your normal content here ...
   <SomeLayout... />
    ... the bottom sheet with the behavior
    <LinearLayout
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:orientation="vertical"
        app:layout_behavior="<com.package.name.of.the.class>.AutoCloseBottomSheetBehavior">
        ... the bottom sheet views
    </LinearLayout>
</android.support.design.widget.CoordinatorLayout>
- 39,391
 - 16
 - 102
 - 144
 
- 
                    7This works great! Small piece of advice... adding 'return true' after the call to 'setState' will stop clicks from passing through. In my use case, if clicking outside the dialog, I just wanted it to collapse without passing the click event to view behind it :) – Psest328 Apr 10 '19 at 16:27
 - 
                    1@Psest328 adding `return true` caused this to stop working for me *sometimes*. If I had the `return true` in there the sheet would only occasionally close. – Emil S. Nov 19 '20 at 21:46
 - 
                    
 
For Activity:
@Override 
public boolean dispatchTouchEvent(MotionEvent event){
    if (event.getAction() == MotionEvent.ACTION_DOWN) {
        if (mBottomSheetBehavior.getState()==BottomSheetBehavior.STATE_EXPANDED) {
            Rect outRect = new Rect();
            bottomSheet.getGlobalVisibleRect(outRect);
            if(!outRect.contains((int)event.getRawX(), (int)event.getRawY()))
                mBottomSheetBehavior.setState(BottomSheetBehavior.STATE_COLLAPSED);
        }
    }
    return super.dispatchTouchEvent(event);
}
For Fragment: Use same method in Activity like,
@Override
public boolean dispatchTouchEvent(MotionEvent event) {
    if (event.getAction() == MotionEvent.ACTION_DOWN) {
        if (fragment != null && fragment instanceof HomeFragment) {
            ((HomeFragment) fragment).hideBottomSheetFromOutSide(event);
        }
    }
    return super.dispatchTouchEvent(event);
}
and Create Method in Fragment like:
   /**
     * Calling from Dashboard Activity
     *
     * @param event Motion Event
     */
    public void hideBottomSheetFromOutSide(MotionEvent event) {
        if (isBottomSheetMenuExpanded()) {
            Rect outRect = new Rect();
            mBinding.homeBottomSheetLayout.getGlobalVisibleRect(outRect);
            if (!outRect.contains((int) event.getRawX(), (int) event.getRawY()))
                mBottomSheetBehavior.setState(BottomSheetBehavior.STATE_COLLAPSED);
        }
    }
Hope it will helps you.
Thank you.
- 60,504
 - 58
 - 273
 - 437
 
- 
                    
 - 
                    
 - 
                    
 - 
                    It works but is that a good solution ? Because each time we touch or scroll the screen, that method `dispatchTouchEvent` is called serveral times wich may be bad for battery usage and application performance. – ankalagba Jun 05 '21 at 20:42
 - 
                    Yeah, that's right. Do let me know if you find any other good solution. – Pratik Butani Jun 07 '21 at 04:43
 
Set a on click listener for your main layout (Coordinate layout in this case)
@OnClick(R.id.coordinateLayout)
public void onClickView(View view) {
    if (sheetBehavior.getState() == BottomSheetBehavior.STATE_EXPANDED) {
        sheetBehavior.setState(BottomSheetBehavior.STATE_COLLAPSED);
    }
}
Note : Butterknife is used for on click otherwise use the code below in onCreate of the activity.
CoordinateLayout layout = (CoordinateLayout) findViewById(R.id. coordinateLayout);
layout.setOnClickListener(new View.OnClickListener() {
    @Override
    public void onClick(View v) {
        if (sheetBehavior.getState() == BottomSheetBehavior.STATE_EXPANDED) {
            sheetBehavior.setState(BottomSheetBehavior.STATE_COLLAPSED);
        }
    }
});
- 1,341
 - 1
 - 15
 - 33
 
- 507
 - 4
 - 18
 
You can call the following code to close bottom sheet dialog on clicking outside.
BottomSheetDialog dialog = new BottomSheetDialog(context);
dialog.setContentView(R.layout.bottom_sheet);
dialog.setCanceledOnTouchOutside(true);
dialog.show(); 
- 2,028
 - 1
 - 18
 - 30
 
someViewToClickOn.setOnClickListener(v -> 
    behavior.setState(BottomSheetBehavior.STATE_HIDDEN));
This works too! I first used BottomSheetBehavior.STATE_COLLAPSED which doesn't work
- 7,010
 - 4
 - 54
 - 68
 
There are many peoples finding a way to implement dispatchTouchEvent on fragment. So here's how they can do this:
create a custom layout as defined:
public class DispatchTouchEvent extends LinearLayout {
    public interface onDispatchEvent
    {
        void dispatchEvent(MotionEvent e);
    }
    private onDispatchEvent dispatchEvent;
    public DispatchTouchEvent(Context context) {
        super(context);
    }
    public DispatchTouchEvent(Context context, AttributeSet attrs) {
        super(context, attrs);
    }
    public DispatchTouchEvent(Context context, AttributeSet attrs, int defStyleAttr) {
        super(context, attrs, defStyleAttr);
    }
    public void setDispatchEvent(onDispatchEvent dispatchEvent)
    {
        this.dispatchEvent=dispatchEvent;
    }
    @Override
    public boolean dispatchTouchEvent(MotionEvent ev) {
        if(dispatchEvent!=null)
        {
            dispatchEvent.dispatchEvent(ev);
        }
        return super.dispatchTouchEvent(ev);
    }
}
Now use this layout as the base of your fragment layout. Inside fragment initialise this layout as:
public class ABC extends fragment implements DispatchTouchEvent.onDispatchEvent
{
DispatchTouchEvent dispatchTouchEvent;
@Override
public View onCreateView(LayoutInflater inflater, ViewGroup container,
                         Bundle savedInstanceState) {
....
    dispatchTouchEvent = (DispatchTouchEvent)rootView.findViewById(R.id.dispatch_event);
    dispatchTouchEvent.setDispatchEvent(this);
....
}
@Override
public void dispatchEvent(MotionEvent event) {
    if (event.getAction() == MotionEvent.ACTION_DOWN) {
    if (mBottomSheetBehavior.getState()==BottomSheetBehavior.STATE_EXPANDED) 
    {
        Rect outRect = new Rect();
        bottomSheet.getGlobalVisibleRect(outRect);
        if(!outRect.contains((int)event.getRawX(), (int)event.getRawY()))   
         mBottomSheetBehavior.setState(BottomSheetBehavior.STATE_COLLAPSED);
    }
    }
    }
}
- 2,231
 - 3
 - 23
 - 34
 
I found UX issues with using the OP's answer or the AutoCloseBottomSheetBehavior.java version (from Budius). I changed the AutoCloseBottomSheetBehavior code in a way which seems cleaner to me and doesn't cause any UX issues (code is in Kotlin):
class AutoCloseBottomSheetBehavior<V : View>(
        context: Context,
        attrs: AttributeSet
) : BottomSheetBehavior<V>(context, attrs) {
    private val gestureDetector: GestureDetectorCompat =
            GestureDetectorCompat(context, object : GestureDetector.SimpleOnGestureListener() {
                override fun onDown(event: MotionEvent?): Boolean {
                    return true
                }
                override fun onSingleTapUp(e: MotionEvent?): Boolean =
                        when {
                            state == STATE_EXPANDED -> {
                                state = STATE_COLLAPSED
                                true
                            }
                            state == STATE_COLLAPSED && isHideable -> {
                                state = STATE_HIDDEN
                                true
                            }
                            else -> false
                        }
            })
    @SuppressLint("ClickableViewAccessibility")
    override fun onLayoutChild(parent: CoordinatorLayout, child: V, layoutDirection: Int): Boolean {
        parent.setOnTouchListener { _, event ->
            gestureDetector.onTouchEvent(event)
        }
        return super.onLayoutChild(parent, child, layoutDirection)
    }
}
This collapses/hides the bottom sheet whenever the user performs a single tap on the parent CoordinatorLayout and also handles the case in which the bottom sheet is hidable (and we want to hide it if it's in a COLLAPSED state).
- 30,962
 - 25
 - 85
 - 135
 
- 21
 - 4
 
for me it was a simple setCancelable(true); i.e
@Override
public void setupDialog(Dialog dialog, int style) {
    super.setupDialog(dialog, style);
    View contentView = View.inflate(getContext(), R.layout.layout_additional_prices, null);
    unbinder = ButterKnife.bind(this, contentView);
    dialog.setContentView(contentView);
    dialog.setOnKeyListener(new BottomSheetBackDismissListener());
    //makeBottomSheetFullScreen(getActivity(), mBottomSheetBehaviorCallback, contentView);
    setCancelable(true);
}
- 3,382
 - 28
 - 31
 
There is two methods to make dialog is Cancelable :
Sets whether this dialog is cancelable with the BACK key.
Java
dialog.setCancelable(true);
Kotlin
dialog.setCancelable(true)
Sets whether this dialog is canceled when touched outside the window's bounds.
Java
dialog.setCanceledOnTouchOutside(true);
Kotlin
dialog.setCanceledOnTouchOutside(true)
- 692
 - 10
 - 15