Disabling User dragging on BottomSheet
implementation 'com.google.android.material:material:1.2.0-alpha05'
you can disable dragging the BottomSheet like this.
override fun onCreateDialog(savedInstanceState: Bundle?): Dialog {
val dialog = BottomSheetDialog(requireContext(), theme)
dialog.setOnShowListener {
setBottomSheetExpanded(dialog)
}
return dialog
}
open fun setBottomSheetExpanded(bottomSheetDialog: BottomSheetDialog) {
val bottomSheet =
bottomSheetDialog.findViewById<View>(R.id.design_bottom_sheet) as FrameLayout?
bottomSheet?.let {
val behavior: BottomSheetBehavior<*> = BottomSheetBehavior.from(bottomSheet)
val layoutParams = bottomSheet.layoutParams
bottomSheet.layoutParams = layoutParams
behavior.state = BottomSheetBehavior.STATE_EXPANDED
behavior.isDraggable = false / true
}
}
Edited) The library was updated! you can use new library version
implementation 'com.google.android.material:material:1.4.0'
The examples are the same, good luck and good code
Alright, so the accepted answer didn't work for me. However, Виталий Обидейко's answer inspired my final solution.
First, I created the following custom BottomSheetBehavior. It overrides all of the methods involving touch, and returns false (or did nothing) if it is locked. Otherwise, it acts like a normal BottomSheetBehavior. This disables the user's ability to drag down, and does not affect changing the state in code.
LockableBottomSheetBehavior.java
public class LockableBottomSheetBehavior<V extends View> extends BottomSheetBehavior<V> {
private boolean mLocked = false;
public LockableBottomSheetBehavior() {}
public LockableBottomSheetBehavior(Context context, AttributeSet attrs) {
super(context, attrs);
}
public void setLocked(boolean locked) {
mLocked = locked;
}
@Override
public boolean onInterceptTouchEvent(CoordinatorLayout parent, V child, MotionEvent event) {
boolean handled = false;
if (!mLocked) {
handled = super.onInterceptTouchEvent(parent, child, event);
}
return handled;
}
@Override
public boolean onTouchEvent(CoordinatorLayout parent, V child, MotionEvent event) {
boolean handled = false;
if (!mLocked) {
handled = super.onTouchEvent(parent, child, event);
}
return handled;
}
@Override
public boolean onStartNestedScroll(CoordinatorLayout coordinatorLayout, V child, View directTargetChild, View target, int nestedScrollAxes) {
boolean handled = false;
if (!mLocked) {
handled = super.onStartNestedScroll(coordinatorLayout, child, directTargetChild, target, nestedScrollAxes);
}
return handled;
}
@Override
public void onNestedPreScroll(CoordinatorLayout coordinatorLayout, V child, View target, int dx, int dy, int[] consumed) {
if (!mLocked) {
super.onNestedPreScroll(coordinatorLayout, child, target, dx, dy, consumed);
}
}
@Override
public void onStopNestedScroll(CoordinatorLayout coordinatorLayout, V child, View target) {
if (!mLocked) {
super.onStopNestedScroll(coordinatorLayout, child, target);
}
}
@Override
public boolean onNestedPreFling(CoordinatorLayout coordinatorLayout, V child, View target, float velocityX, float velocityY) {
boolean handled = false;
if (!mLocked) {
handled = super.onNestedPreFling(coordinatorLayout, child, target, velocityX, velocityY);
}
return handled;
}
}
Here's an example of how to use it. In my case, I needed it so the Bottom Sheet locked when expanded.
activity_home.xml
<?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"
android:layout_width="match_parent"
android:layout_height="match_parent">
<android.support.design.widget.AppBarLayout
android:layout_width="match_parent"
android:layout_height="wrap_content">
<android.support.design.widget.CollapsingToolbarLayout
android:layout_width="match_parent"
android:layout_height="match_parent"
app:layout_scrollFlags="scroll|snap"
app:titleEnabled="false"/>
<android.support.v7.widget.Toolbar
android:id="@+id/toolbar"
android:layout_width="match_parent"
android:layout_height="?attr/actionBarSize"/>
</android.support.design.widget.AppBarLayout>
<!-- Use layout_behavior to set your Behavior-->
<android.support.v7.widget.RecyclerView
android:id="@+id/recyclerview"
android:layout_width="match_parent"
android:layout_height="match_parent"
app:layoutManager="android.support.v7.widget.LinearLayoutManager"
app:layout_behavior="com.myapppackage.LockableBottomSheetBehavior"/>
</android.support.design.widget.CoordinatorLayout>
HomeActivity.java
public class HomeActivity extends AppCompatActivity {
BottomSheetBehavior mBottomSheetBehavior;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_home);
RecyclerView recyclerView = (RecyclerView) findViewById(R.id.recyclerview);
recyclerView.setAdapter(new SomeAdapter());
mBottomSheetBehavior = BottomSheetBehavior.from(recyclerView);
mBottomSheetBehavior.setBottomSheetCallback(new MyBottomSheetCallback());
}
class MyBottomSheetCallback extends BottomSheetBehavior.BottomSheetCallback() {
@Override
public void onStateChanged(@NonNull View bottomSheet, int newState) {
if (newState == BottomSheetBehavior.STATE_EXPANDED) {
if (mBottomSheetBehavior instanceof LockableBottomSheetBehavior) {
((LockableBottomSheetBehavior) mBottomSheetBehavior).setLocked(true);
}
}
}
@Override
public void onSlide(@NonNull View bottomSheet, float slideOffset) {}
});
}
Hope this helps clear up a lot of the confusion!
It can be now no longer relevant, but I will leave it here:
import android.content.Context
import android.util.AttributeSet
import androidx.coordinatorlayout.widget.CoordinatorLayout
import android.view.MotionEvent
import android.view.View
import com.google.android.material.bottomsheet.BottomSheetBehavior
@Suppress("unused")
class LockableBottomSheetBehavior<V : View> : BottomSheetBehavior<V> {
constructor() : super()
constructor(context: Context, attrs: AttributeSet) : super(context, attrs)
var swipeEnabled = true
override fun onInterceptTouchEvent(
parent: CoordinatorLayout,
child: V,
event: MotionEvent
): Boolean {
return if (swipeEnabled) {
super.onInterceptTouchEvent(parent, child, event)
} else {
false
}
}
override fun onTouchEvent(parent: CoordinatorLayout, child: V, event: MotionEvent): Boolean {
return if (swipeEnabled) {
super.onTouchEvent(parent, child, event)
} else {
false
}
}
override fun onStartNestedScroll(
coordinatorLayout: CoordinatorLayout,
child: V,
directTargetChild: View,
target: View,
axes: Int,
type: Int
): Boolean {
return if (swipeEnabled) {
super.onStartNestedScroll(
coordinatorLayout,
child,
directTargetChild,
target,
axes,
type
)
} else {
false
}
}
override fun onNestedPreScroll(
coordinatorLayout: CoordinatorLayout,
child: V,
target: View,
dx: Int,
dy: Int,
consumed: IntArray,
type: Int
) {
if (swipeEnabled) {
super.onNestedPreScroll(coordinatorLayout, child, target, dx, dy, consumed, type)
}
}
override fun onStopNestedScroll(
coordinatorLayout: CoordinatorLayout,
child: V,
target: View,
type: Int
) {
if (swipeEnabled) {
super.onStopNestedScroll(coordinatorLayout, child, target, type)
}
}
override fun onNestedPreFling(
coordinatorLayout: CoordinatorLayout,
child: V,
target: View,
velocityX: Float,
velocityY: Float
): Boolean {
return if (swipeEnabled) {
super.onNestedPreFling(coordinatorLayout, child, target, velocityX, velocityY)
} else {
false
}
}
}
And use it in your xml file:
app:layout_behavior="com.your.package.LockableBottomSheetBehavior"
It disables all users actions, it can be used when you want control BottomSheet only programmatically.
check state in onStateChanged
method of setBottomSheetCallback
if state is BottomSheetBehavior.STATE_DRAGGING
then change it to BottomSheetBehavior.STATE_EXPANDED
this way you can stop STATE_DRAGGING
by user. like below
final BottomSheetBehavior behavior = BottomSheetBehavior.from(bottomSheet);
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) {
}
});
use button to open close bottom sheet like below
fab.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View view) {
if (behavior.getState() == BottomSheetBehavior.STATE_HIDDEN) {
behavior.setState(BottomSheetBehavior.STATE_EXPANDED);
} else {
behavior.setState(BottomSheetBehavior.STATE_COLLAPSED);
}
}
});
don't use setPeekHeight
or app:behavior_peekHeight
by above way you can reach your goal