Deshabilitar el arrastre del usuario en BottomSheet

Estoy tratando de deshabilitar el arrastre del usuario en BottomSheet . La razón por la que quiero deshabilitar es dos cosas. 1. ListView desplace hacia abajo, 2. No deseo que los usuarios descarten el uso del arrastre, pero con un botón en BottomSheetView . Esto es lo que hice

  bottomSheetBehavior = BottomSheetBehavior.from(bottomAnc); bottomSheetBehavior.setBottomSheetCallback(new BottomSheetBehavior.BottomSheetCallback() { @Override public void onStateChanged(@NonNull View bottomSheet, int newState) { if (newState == BottomSheetBehavior.STATE_EXPANDED) { //Log.e("BottomSheet", "Expanded"); } else if (newState == BottomSheetBehavior.STATE_COLLAPSED) { //Log.e("BottomSheet", "Collapsed"); } } @Override public void onSlide(@NonNull View bottomSheet, float slideOffset) { // React to dragging events bottomSheet.setOnTouchListener(new View.OnTouchListener() { @Override public boolean onTouch(View v, MotionEvent event) { int action = MotionEventCompat.getActionMasked(event); switch (action) { case MotionEvent.ACTION_DOWN: return false; default: return true; } } }); } }); 

The bottomSheetLayout

       

Ahora puede dejar de ser relevante, pero lo dejaré aquí:

 import android.content.Context; import android.support.design.widget.BottomSheetBehavior; import android.support.design.widget.CoordinatorLayout; import android.util.AttributeSet; import android.view.MotionEvent; import android.view.View; /** * Created by vitaliiobideiko on 10/5/16. */ public class UserLockBottomSheetBehavior extends BottomSheetBehavior { public UserLockBottomSheetBehavior() { super(); } public UserLockBottomSheetBehavior(Context context, AttributeSet attrs) { super(context, attrs); } @Override public boolean onInterceptTouchEvent(CoordinatorLayout parent, V child, MotionEvent event) { return false; } @Override public boolean onTouchEvent(CoordinatorLayout parent, V child, MotionEvent event) { return false; } @Override public boolean onStartNestedScroll(CoordinatorLayout coordinatorLayout, V child, View directTargetChild, View target, int nestedScrollAxes) { return false; } @Override public void onNestedPreScroll(CoordinatorLayout coordinatorLayout, V child, View target, int dx, int dy, int[] consumed) {} @Override public void onStopNestedScroll(CoordinatorLayout coordinatorLayout, V child, View target) {} @Override public boolean onNestedPreFling(CoordinatorLayout coordinatorLayout, V child, View target, float velocityX, float velocityY) { return false; } } 

Deshabilita todas las acciones de los usuarios, puede utilizarse cuando desee controlar BottomSheet solo mediante progtwigción. Puede editarlo y crear un indicador booleano para llamar a los super lugar de return false .

compruebe el estado en el método onStateChanged de setBottomSheetCallback si el estado es BottomSheetBehavior.STATE_DRAGGING luego cámbielo a BottomSheetBehavior.STATE_EXPANDED esta manera puede detener STATE_DRAGGING por el usuario. como abajo

 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) { } }); 

utilice el botón para abrir la hoja de cierre inferior como a continuación

 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); } } }); 

no use setPeekHeight o la app:behavior_peekHeight

por arriba puedes alcanzar tu objective

Terminé escribiendo una solución para abordar este caso de uso de deshabilitar dinámicamente el arrastre del usuario, donde BottomSheetBehavior está subclasificado para reemplazar onInterceptTouchEvent, y para ignorarlo cuando un marcador personalizado (en este caso, mAllowUserDragging) se establece en falso:

 import android.content.Context; 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 WABottomSheetBehavior extends BottomSheetBehavior { private boolean mAllowUserDragging = true; /** * Default constructor for instantiating BottomSheetBehaviors. */ public WABottomSheetBehavior() { super(); } /** * Default constructor for inflating BottomSheetBehaviors from layout. * * @param context The {@link Context}. * @param attrs The {@link AttributeSet}. */ public WABottomSheetBehavior(Context context, AttributeSet attrs) { super(context, attrs); } public void setAllowUserDragging(boolean allowUserDragging) { mAllowUserDragging = allowUserDragging; } @Override public boolean onInterceptTouchEvent(CoordinatorLayout parent, V child, MotionEvent event) { if (!mAllowUserDragging) { return false; } return super.onInterceptTouchEvent(parent, child, event); } } 

Y en su diseño xml:

   

Hasta el momento, esta es la solución de comportamiento más consistente para deshabilitar el arrastre del usuario en la hoja inferior bajo demanda.

Todas las demás soluciones que dependían de activar otra llamada setState en la callback onStateChanged provocaron que BottomSheet entrase en mal estado o causara problemas significativos de UX (en el caso de publicar la llamada a setState en Runnable).

Espero que esto ayude a alguien 🙂

Rayo

Bien, entonces la respuesta aceptada no funcionó para mí. Sin embargo, la respuesta de Виталий Обидейко inspiró mi solución final.

Primero, creé el siguiente comportamiento personalizado de BottomSheetBehavior. Anula todos los métodos que implican toque y devuelve falso (o no hizo nada) si está bloqueado. De lo contrario, actúa como un Comportamiento de hoja inferior normal. Esto desactiva la capacidad del usuario para arrastrar hacia abajo y no afecta el cambio de estado en el código.

LockableBottomSheetBehavior.java

 public class LockableBottomSheetBehavior extends BottomSheetBehavior { 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; } } 

Aquí hay un ejemplo de cómo usarlo. En mi caso, lo necesitaba para que la Hoja Inferior se bloqueara cuando se expandiera.

activity_home.xml

 < ?xml version="1.0" encoding="utf-8"?>         

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) {} }); } 

Espero que esto ayude a aclarar la confusión.

La respuesta aceptada no funciona en el primer dispositivo de prueba que uso. Y el rebote no es suave. Parece mejor establecer el estado en STATE_EXPANDED solo después de que un usuario suelta el arrastre. La siguiente es mi versión:

  final BottomSheetBehavior behavior = BottomSheetBehavior.from(findViewById(R.id.bottomSheet)); behavior.setBottomSheetCallback(new BottomSheetBehavior.BottomSheetCallback() { @Override public void onStateChanged(@NonNull View bottomSheet, int newState) { if (newState > BottomSheetBehavior.STATE_DRAGGING) bottomSheet.post(new Runnable() { @Override public void run() { behavior.setState(BottomSheetBehavior.STATE_EXPANDED); } }); } @Override public void onSlide(@NonNull View bottomSheet, float slideOffset) { } }); 

Para bloquear la hoja inferior y evitar que el usuario la deslice, esto es lo que hice

 public void showBottomSheet() { bsb.setHideable(false); bsb.setState(BottomSheetBehavior.STATE_EXPANDED); } public void hideBottomSheet() { bsb.setHideable(true); bsb.setState(BottomSheetBehavior.STATE_COLLAPSED); } 

Funciona bastante bien para mí.

Una forma fácil de bloquear el arrastre es setPeekHeight igual que la altura de la vista. Por ejemplo:

 private LinearLayout bottomSheet; private BottomSheetBehavior bottomBehavior; @Override public void onResume() { super.onResume(); bottomBehavior = BottomSheetBehavior.from((bottomSheet); bottomBehavior.setPeekHeight(bottomSheet.getHeight()); bottomBehavior.setState(BottomSheetBehavior.STATE_EXPANDED); } 

He encontrado una solución increíble. El problema inicial fue que bottomSheet iba a estado HIDDEN y luego no se mostraba en bottomSheetDialog.show (). Pero quería que el diálogo fuera visible en el método show () y también quería que el usuario lo deslizara hacia abajo para que se sintiera como una hoja inferior. Debajo está lo que hice …

  BottomSheetDialog itemTypeDialog = new BottomSheetDialog(this); View bottomSheetView = getLayoutInflater().inflate(R.layout.dialog_bottomsheet, null); itemTypeDialog.setContentView(bottomSheetView); BottomSheetBehavior bottomSheetBehavior = BottomSheetBehavior.from((View) bottomSheetView.getParent()); bottomSheetBehavior.setBottomSheetCallback(bottomSheetCallback); // You can also attach the listener here itself. BottomSheetBehavior.BottomSheetCallback bottomSheetCallback = new BottomSheetBehavior.BottomSheetCallback() { @Override public void onStateChanged(@NonNull View bottomSheet, int newState) { Log.d(TAG, "BottomSheetCallback: " + newState); if (newState == BottomSheetBehavior.STATE_HIDDEN) { bottomSheetBehavior.setState(BottomSheetBehavior.STATE_COLLAPSED); itemTypeDialog.dismiss(); } } @Override public void onSlide(@NonNull View bottomSheet, float slideOffset) { } }; 

No necesita bloquear todos los eventos cuando la hoja inferior está desactivada. Puede bloquear solo el evento ACTION_MOVE. Es por eso que usa un comportamiento de hoja inferior personalizado como este

 public class BottomSheetBehaviorWithDisabledState extends BottomSheetBehavior { private boolean enable = true; /** * Default constructor for instantiating BottomSheetBehaviors. */ public BottomSheetBehaviorWithDisabledState() { super(); } /** * Default constructor for inflating BottomSheetBehaviors from layout. * * @param context The {@link Context}. * @param attrs The {@link AttributeSet}. */ public BottomSheetBehaviorWithDisabledState(Context context, AttributeSet attrs) { super(context, attrs); } public void setEnable(boolean enable){ this.enable = enable; } @Override public boolean onInterceptTouchEvent(CoordinatorLayout parent, V child, MotionEvent event) { if (!enable && event.getAction() == MotionEvent.ACTION_MOVE){ return false; } return super.onInterceptTouchEvent(parent, child, event); } } 

Respuesta tardía, pero, esto es lo que funcionó para mí, que es un poco diferente de lo que otros han sugerido.

Podría intentar establecer la propiedad cancelable en falso, es decir,

 setCancelable(false); 

y luego manejando manualmente los eventos donde le gustaría cerrar el diálogo en el método setupDialog .

 @Override public void setupDialog(final Dialog dialog, final int style) { // handle back button dialog.setOnKeyListener(new DialogInterface.OnKeyListener() { @Override public boolean onKey(final DialogInterface dialog, final int keyCode, final KeyEvent event) { if (keyCode == KeyEvent.KEYCODE_BACK) { dialog.dismiss(); } return true; } }); // handle touching outside of the dialog final View touchOutsideView = getDialog().getWindow().getDecorView().findViewById(android.support.design.R.id.touch_outside); touchOutsideView.setOnClickListener(new View.OnClickListener() { @Override public void onClick(final View view) { dialog.dismiss(); } }); } 

Esto funciona con un ListView dentro del fragmento de diálogo, que era donde me estaba quedando un poco atrapado con otras soluciones.

Agregue este código al objeto BottomSheetBehavior . Arrastrar estará deshabilitado. Funciona bien para mí

Comportamiento final del Comportamiento de la Capa Inferior = Comportamiento de la Hoja Inferior.desde ((Ver) vista.getParent ()); behavior.setHideable (falso); behavior.setBottomSheetCallback (new BottomSheetBehavior.BottomSheetCallback () {

  @Override public void onStateChanged(@NonNull View bottomSheet, int newState) { if (newState == BottomSheetBehavior.STATE_DRAGGING) { behavior.setState(BottomSheetBehavior.STATE_COLLAPSED); } } @Override public void onSlide(@NonNull View bottomSheet, float slideOffset) { } }); 
  1. Copie BottomSheetDialog en su proyecto y cambie el nombre a MyBottomSheetDialog
  2. agregue getBottomSheetBehavior a MyBottomSheetDialog
  3. use MyBottomSheetDialog en BottomSheetDialog lugar BottomSheetDialog
  4. bottomSheetBehavior.setBottomSheetCallback

código como este

 public class MyBottomSheetDialog extends AppCompatDialog { // some code public BottomSheetBehavior getBottomSheetBehavior() { return mBehavior; } // more code 

en tu código

  final BottomSheetBehavior bottomSheetBehavior = myBottomSheetDialog.getBottomSheetBehavior(); bottomSheetBehavior.setBottomSheetCallback(new BottomSheetBehavior.BottomSheetCallback() { @Override public void onStateChanged(@NonNull View bottomSheet, int newState) { if (newState == BottomSheetBehavior.STATE_DRAGGING) { bottomSheetBehavior.setState(BottomSheetBehavior.STATE_EXPANDED); } } @Override public void onSlide(@NonNull View bottomSheet, float slideOffset) { } 

Esta es básicamente la versión de kotlin de la respuesta correcta en la parte superior:

 class UserLockBottomSheetBehavior : BottomSheetBehavior { companion object { fun  from(view: V): UserLockBottomSheetBehavior { val params = view.layoutParams as? CoordinatorLayout.LayoutParams ?: throw IllegalArgumentException("The view is not a child of CoordinatorLayout") val behavior = params.behavior as? UserLockBottomSheetBehavior< *> ?: throw IllegalArgumentException( "The view is not associated with BottomSheetBehavior") return behavior as UserLockBottomSheetBehavior } } constructor(ctx: Context, attrs: AttributeSet) : super(ctx, attrs) override fun onInterceptTouchEvent(parent: CoordinatorLayout, child: V, event: MotionEvent): Boolean { return false } override fun onTouchEvent(parent: CoordinatorLayout, child: V, event: MotionEvent): Boolean { return false } override fun onStartNestedScroll(coordinatorLayout: CoordinatorLayout, child: V, directTargetChild: View, target: View, axes: Int, type: Int): Boolean { return false } override fun onNestedPreScroll(coordinatorLayout: CoordinatorLayout, child: V, target: View, dx: Int, dy: Int, consumed: IntArray, type: Int) {} override fun onStopNestedScroll(coordinatorLayout: CoordinatorLayout, child: V, target: View, type: Int) {} override fun onNestedPreFling(coordinatorLayout: CoordinatorLayout, child: V, target: View, velocityX: Float, velocityY: Float): Boolean { return false } } 

Aquí hay una versión de trabajo de la mejor solución en Kotlin:

 import android.support.design.widget.BottomSheetBehavior import android.support.design.widget.CoordinatorLayout import android.view.MotionEvent import android.view.View class CustomBottomSheetBehavior : BottomSheetBehavior() { @Suppress("UNCHECKED_CAST") companion object { fun  from(view: V): CustomBottomSheetBehavior { val params = view.layoutParams as? CoordinatorLayout.LayoutParams ?: throw IllegalArgumentException("The view is not a child of CoordinatorLayout") params.behavior as? BottomSheetBehavior ?: throw IllegalArgumentException("The view is not associated with BottomSheetBehavior") params.behavior = CustomBottomSheetBehavior() return params.behavior as CustomBottomSheetBehavior } } override fun onInterceptTouchEvent(parent: CoordinatorLayout, child: V, event: MotionEvent): Boolean { return false } override fun onTouchEvent(parent: CoordinatorLayout, child: V, event: MotionEvent): Boolean { return false } override fun onStartNestedScroll(coordinatorLayout: CoordinatorLayout, child: V, directTargetChild: View, target: View, axes: Int, type: Int): Boolean { return false } override fun onNestedPreScroll(coordinatorLayout: CoordinatorLayout, child: V, target: View, dx: Int, dy: Int, consumed: IntArray, type: Int) {} override fun onStopNestedScroll(coordinatorLayout: CoordinatorLayout, child: V, target: View, type: Int) {} override fun onNestedPreFling(coordinatorLayout: CoordinatorLayout, child: V, target: View, velocityX: Float, velocityY: Float): Boolean { return false } } 

Entonces siempre que quieras usar:

 val bottomSheetBehavior by lazy { CustomBottomSheetBehavior.from(bottom_sheet_main) } 

El bottom_sheet_main es la vista real usando Kotlin Android Extensions .

establece bottomSheet onClickListener en nulo.

 bottomSheet.setOnClickListener(null); 

esta línea deshabilita todas las acciones sobre bottomSheet solamente y no afecta a la vista interna.