Deshabilitar el arrastre del usuario en la hoja inferior

99

Estoy intentando deshabilitar que el usuario se arrastre BottomSheet. La razón por la que quiero inhabilitar son dos cosas. 1. Evita que se ListViewdesplace hacia abajo, 2. No quiero que los usuarios descarten el uso de arrastrar pero con un botón en BottomSheetView. Esto es lo que he hecho

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

El bottomSheetLayout

    <?xml version="1.0" encoding="utf-8"?><FrameLayout
xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
android:orientation="vertical"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:background="@color/white"
app:behavior_hideable="true"
app:behavior_peekHeight="0dp"
app:layout_behavior="@string/bottom_sheet_behavior"
android:id="@+id/bottomSheet">

<android.support.v7.widget.CardView
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    app:elevation="10dp">

    <LinearLayout
        android:layout_width="match_parent"
        android:layout_height="match_parent"
        android:orientation="vertical">

        <LinearLayout
            android:layout_width="match_parent"
            android:layout_height="wrap_content"
            android:orientation="horizontal"
            android:gravity="center_vertical">

            <TextView
                android:id="@+id/text1"
                android:layout_width="0dp"
                android:layout_height="wrap_content"
                android:layout_weight="1"
                android:text="Order Items"
                android:layout_margin="16dp"
                android:textAppearance="@android:style/TextAppearance.Large"/>


            <Button
                android:layout_width="50dp"
                android:layout_height="wrap_content"
                android:layout_marginRight="5dp"
                android:background="@drawable/bg_accept"/>

            <Button
                android:layout_width="50dp"
                android:layout_height="wrap_content"
                android:layout_marginRight="8dp"
                android:background="@drawable/bg_cancel"/>

        </LinearLayout>

        <ListView
            android:id="@+id/item_edit"
            android:layout_width="match_parent"
            android:layout_height="match_parent"
            android:background="@color/white"
            android:divider="@color/md_divider_black"
            android:dividerHeight="1dp"/>

    </LinearLayout>

</android.support.v7.widget.CardView>

Abubakar Oladeji
fuente
Por favor, revisa mi respuesta. He notado que es más relevante que la respuesta aceptada
Vitalii Obideiko

Respuestas:

92

Puede que ahora ya no sea relevante, pero lo dejaré aquí:

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

Y utilícelo en su archivo xml:

app:layout_behavior="com.your.package.LockableBottomSheetBehavior"

Deshabilita todas las acciones de los usuarios, se puede usar cuando desee controlar BottomSheet solo mediante programación.

Vitalii Obideiko
fuente
2
Esta es la mejor respuesta para deshabilitar BottomSheetBehaviour. Un hombre de arriba también publicó una solución similar, pero no escribió para anular otros eventos como onTouchEvent () . Por otro lado, puede mejorar su respuesta si coloca una bandera en lugar de falso
murt
3
¿Cómo se usa esto con un BottomSheetFragment?
user3144836
7
Debe hacer referencia específicamente a esta clase en su XML. app: layout_behavior = "com.my.package.UserLockBottomSheetBehavior"
Steve
3
En algunos casos, esto todavía no funciona, si tenemos una lista en el fragmento de la hoja inferior, todavía se arrastra
Deepak Joshi
1
@DeepakJoshi tal vez puedas extender RecyclerView y anular algunos métodos como 'hasNestedScrollingParent', pero no estoy seguro
Vitalii Obideiko
74

compruebe el estado en el onStateChangedmétodo de setBottomSheetCallbacksi el estado es y BottomSheetBehavior.STATE_DRAGGINGluego cámbielo de BottomSheetBehavior.STATE_EXPANDEDesta manera que puede detener STATE_DRAGGINGpor 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) {
            }
        });

use el botón para abrir cerrar la hoja inferior como se muestra 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 setPeekHeightoapp:behavior_peekHeight

por la forma anterior puedes alcanzar tu meta

Dhaval Parmar
fuente
1
Buen truco. No me di cuenta de eso. Gracias. Y también, ¿puedes ayudar con esto? Cuando le digo que se expanda al principio, es transparente y puedo ver la vista detrás, pero no puedo interactuar hasta que toque el EditText en SheetView antes de hacerlo visible.
Abubakar Oladeji
Hice mi BottomSheet View match_parenty cada vez que trato de abrirlo en mi Activity, noté que se desliza hacia arriba, pero no es visible hasta que lo EditTextKeyboardBottomSheet View
toco,
1
Intenté esto, pero los estados terminan en STATE_SETTLING. Tengo un botón para abrir y cerrar la hoja inferior, si está OCULTO, lo amplío. Si está EXPANDIDO, lo oculto. Como se atasca en SETTLING, mi botón no funciona después de arrastrar la hoja inferior. ¿Alguna idea sobre eso?
Gokhan Arik
3
Esta solución no es confiable; la hoja inferior se pone en mal estado, como dijo Gokhan ... y cuando está en ese mal estado, las llamadas como cargar un nuevo fragmento en la hoja inferior simplemente se borrarán.
Ray W
7
No funcionará si ha anidado la vista de desplazamiento dentro de la hoja inferior
Rishabh Chandel
32

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

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

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;

    }
}

Aquí tienes un ejemplo de cómo usarlo. En mi caso, lo necesitaba para que la hoja inferior se bloqueara cuando se expandía.

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

¡Espero que esto ayude a aclarar mucha confusión!

James Davis
fuente
1
Bien, la mejor respuesta es que podemos evitar solucionar estos estados que llevan a perder eventos. Gracias.
Tấn Nguyên
@James: buena respuesta, pero ahora no puedo establecerPeekHeight (). ¿Alguna idea?
Adarsh ​​Yadav
Intenté esto. esto funciona para mi. gracias hermano por salvarme el culo
Sup.Ia
1
Esta es una buena solución alternativa, aunque no está actualizada a día de hoy. OnNestedPreScroll y algunos otros métodos han quedado obsoletos. Necesito actualizar esos métodos y funciona bien.
Ajay
4
Hola, no funciona en un BottomSheetDialogFragment, todavía puedo arrastrar la hoja inferior
florian-do
23

Terminé escribiendo una solución para abordar este caso de uso de deshabilitar dinámicamente el arrastre del usuario, mediante el cual BottomSheetBehavior se subclasifica para anular onInterceptTouchEvent, e ignorarlo cuando una marca personalizada (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<V extends View> extends BottomSheetBehavior<V> {
    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:

    <FrameLayout
        android:id="@+id/bottom_sheet_frag_container"
        android:layout_width="match_parent"
        android:layout_height="match_parent"
        app:behavior_hideable="true"
        app:behavior_peekHeight="@dimen/bottom_sheet_peek_height"
        app:elevation="@dimen/bottom_sheet_elevation"
        app:layout_behavior="com.example.ray.WABottomSheetBehavior" />

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

Todas las otras soluciones que se basaron en disparar otra llamada setState en la devolución de llamada onStateChanged dieron como resultado que BottomSheet entrara en un mal estado o causara problemas significativos de UX (en el caso de publicar la llamada setState en un Runnable).

Espero que esto ayude a alguien :)

Rayo

Ray W
fuente
4
Eso es bastante bueno
Odys
3
@BeeingJk En lugar de FrameLayout, use NestedScrollView y configurebottomSheetFragContainer.setNestedScrollingEnabled(false);
AfzalivE
1
SOLUCIONADO: estableciendo la devolución de llamada CoordinatorLayout.Behavior behavior = layoutParams.getBehavior (); if (comportamiento! = nulo && instancia de comportamiento de BottomSheetBehavior) {((comportamiento de (BottomSheetBehavior)) .setBottomSheetCallback (mBottomSheetBehaviorCallback); }
LOG_TAG
3
¡Esto no funciona para mí! PD: tengo un texto desplazable en la hoja inferior
Thorvald Olavsen
6
¿Cómo se lanza durante la inicialización? Esto me da una advertencia WABottomSheetBehavior <View> behavior = (WABottomSheetBehavior) BottomSheetBehavior.from (sheetView);
Leo Droidcoder
8

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

Puede intentar establecer la cancelablepropiedad en falso, es decir

setCancelable(false);

y luego manejando manualmente los eventos donde le gustaría descartar el diálogo en el setupDialogmétodo.

@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 atascado con otras soluciones.

chrisw
fuente
Buena solución concisa. Para cualquiera que lea esto, probablemente querrá verificaciones adicionales para event.isCanceled()y event.getAction() == MotionEvent.ACTION_UPantes de cerrar el cuadro de diálogo; esto evitará que los clics erróneos activen el rechazo.
Eric Bachhuber
Gracias por esto. Esta es la solución más sencilla para deshabilitar el arrastre.
AVJ
7

La respuesta aceptada no funciona en el primer dispositivo de prueba que utilizo. 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) {
        }
    });
mingfai
fuente
1
Déjame decirte el problema de lanzarlo en un corredor a menos que eso sea lo que quieras. No puede descartarlo con un botón porque es necesario arrastrarlo para descartarlo. Y siempre responderá al arrastrar, solo que evitaría que el usuario arrastre para descartar
Abubakar Oladeji
7

Agregue este código al objeto BottomSheetBehavior . El arrastre estará deshabilitado. Funciona bien para mi.

final BottomSheetBehavior behavior = BottomSheetBehavior.from((View) view.getParent());
    behavior.setHideable(false);
    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) {

      }
});
Kaushik R
fuente
1
Esto no inhabilita el deslizamiento. Colapsa completamente la sábana bajera.
Adam Hurwitz
7

Comportamiento esperado:

  • BottomSheet no se cierra al arrastrar hacia abajo
  • BottomSheet se cierra si se toca fuera de la ventana de diálogo

Código:

class MyBottomSheet : BottomSheetDialogFragment () {

   override fun onActivityCreated(savedInstanceState: Bundle?) {
       super.onActivityCreated(savedInstanceState)
       disableBottomSheetDraggableBehavior()
   }

   private fun disableBottomSheetDraggableBehavior() {
      this.isCancelable = false
      this.dialog?.setCanceledOnTouchOutside(true)
   }

 }
Alif
fuente
Por alguna razón, no puedo cerrar el diálogo tocando afuera, pero funciona para deshabilitar el arrastre
Gastón Saillén
5

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 mi.

Diego Ramírez Vásquez
fuente
Esta solución fue atractiva pero extrañamente hace que la hoja inferior aparezca desde la parte superior de la pantalla en lugar de la parte inferior. Sin embargo, desaparece de la forma habitual. Es muy Star Trek.
Tunga
Necesitaba hacer una modificación de la vista y, en su lugar, usar BottomSheetBehavior.STATE_HIDDEN. En tal caso, tampoco debe llamar setPeekHeight(). Esto es mucho menos complicado que otras soluciones aquí.
HolySamosa
4

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);
}
Sergei K
fuente
4

Una muestra con BottomSheetDialogFragment. Funciona perfectamente.

Editar 04/09/2020: Reemplazado depreciado setBottomSheetCallback()conaddBottomSheetCallback()

class FragMenuBDrawer : BottomSheetDialogFragment() {

    ...

    override fun onCreateDialog(savedInstanceState: Bundle?): Dialog {
        val dialog = super.onCreateDialog(savedInstanceState) as BottomSheetDialog

        dialog.setOnShowListener {
            val bottomSheet = (it as BottomSheetDialog).findViewById<View>(com.google.android.material.R.id.design_bottom_sheet) as FrameLayout?
            val behavior = BottomSheetBehavior.from(bottomSheet!!)
            behavior.state = BottomSheetBehavior.STATE_EXPANDED

            behavior.addBottomSheetCallback(object : BottomSheetBehavior.BottomSheetCallback() {
                override fun onStateChanged(bottomSheet: View, newState: Int) {
                    if (newState == BottomSheetBehavior.STATE_DRAGGING) {
                        behavior.state = BottomSheetBehavior.STATE_EXPANDED
                    }
                }

                override fun onSlide(bottomSheet: View, slideOffset: Float) {}
            })
        }

        // Do something with your dialog like setContentView() or whatever
        return dialog
    }

    ...
}
Sattar Hummatli
fuente
3

No es necesario bloquear todos los eventos cuando la hoja inferior está desactivada. Solo puede bloquear el evento ACTION_MOVE. Es por eso que use un comportamiento de hoja inferior personalizado como este

public class BottomSheetBehaviorWithDisabledState<V extends View> extends BottomSheetBehavior<V> {
    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);
    }
}
Ilya
fuente
¿Cómo usas esta clase? Recibo una IllegalArgumentException: la vista no está asociada con BottomSheetBehavior
user3144836
3

Aquí hay una versión funcional 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<V : View> : BottomSheetBehavior<V>() {

    @Suppress("UNCHECKED_CAST")
    companion object {
        fun <V : View> from(view: V): CustomBottomSheetBehavior<V> {
            val params = view.layoutParams as? CoordinatorLayout.LayoutParams ?:
                throw IllegalArgumentException("The view is not a child of CoordinatorLayout")
                params.behavior as? BottomSheetBehavior<V> ?:
                    throw IllegalArgumentException("The view is not associated with BottomSheetBehavior")
                params.behavior = CustomBottomSheetBehavior<V>()
            return params.behavior as CustomBottomSheetBehavior<V>
        }
    }

    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, cuando quieras usar:

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

Esta bottom_sheet_maines la vista real usando las extensiones de Android Kotlin .

Rodrigo Queiroz
fuente
3

establezca bottomSheet onClickListener en nulo.

bottomSheet.setOnClickListener(null);

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

Hashem Jahangiri
fuente
1
Esto provoca una animación inesperada cuando la hoja inferior intenta cerrarse.
Adam Hurwitz
3
implementation 'com.google.android.material:material:1.2.0-alpha05'

puede deshabilitar el arrastre de BottomSheet de esta manera.

import com.google.android.material.bottomsheet.BottomSheetBehavior
import com.google.android.material.bottomsheet.BottomSheetBehavior.STATE_EXPANDED

//another code

this.bottomSheetBehavior = BottomSheetBehavior.from(view)
this.bottomSheetBehavior.state = STATE_EXPANDED
this.bottomSheetBehavior.isDraggable = false // disable dragging

//another code
this.bottomSheetbehavior.isDraggable = true //draggable

(kotlin), espero que esta respuesta pueda resolver su problema.

lgb
fuente
1
Esta versión alfa se comporta locamente. No recomiendo :(
Adam Styrc
2

He encontrado una solución asombrosa. El problema inicial era que una vez que bottomSheet pasaba al estado HIDDEN, no aparecía en bottomSheetDialog.show (). Pero quería que el cuadro de diálogo fuera visible en el método show () y también quería permitir que el usuario lo deslizara hacia abajo para que se sintiera como la última hoja. A continuación se muestra 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) {

    }
};
Deepesh
fuente
esta es una respuesta perfecta
Vivek Kumar Srivastava
2
  1. Copie BottomSheetDialoga su proyecto y cambie el nombre aMyBottomSheetDialog
  2. agregar getBottomSheetBehavioraMyBottomSheetDialog
  3. usar MyBottomSheetDialogen su lugarBottomSheetDialog
  4. bottomSheetBehavior.setBottomSheetCallback

código como este

public class MyBottomSheetDialog extends AppCompatDialog {

    // some code

    public BottomSheetBehavior<FrameLayout> getBottomSheetBehavior() {
        return mBehavior;
    }

    // more code

en tu código

    final BottomSheetBehavior<FrameLayout> 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) {

        }
aotian16
fuente
2

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

    class LockedBottomSheetBehavior<V : View>(context: Context, attrs: AttributeSet) :
        BottomSheetBehavior<V>(context, attrs) {

    companion object {
        fun <V : View> from(view: V): LockedBottomSheetBehavior<*> {
            val params = view.layoutParams as? CoordinatorLayout.LayoutParams
                    ?: throw IllegalArgumentException("The view is not a child of CoordinatorLayout")
            return params.behavior as? LockedBottomSheetBehavior<*>
                    ?: throw IllegalArgumentException(
                            "The view is not associated with BottomSheetBehavior")
        }
    }

    override fun onInterceptTouchEvent(
            parent: CoordinatorLayout,
            child: V, event: MotionEvent
    ) = false

    override fun onTouchEvent(
            parent: CoordinatorLayout,
            child: V,
            event: MotionEvent
    ) = false

    override fun onStartNestedScroll(
            coordinatorLayout: CoordinatorLayout,
            child: V,
            directTargetChild: View,
            target: View,
            axes: Int,
            type: Int) = 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
    ) = false
}
Jamal
fuente
¿Cómo usas esta clase? Recibo una IllegalArgumentException: la vista no está asociada con BottomSheetBehavior
user3144836
1
app: layout_behavior = "UserLockBottomSheetBehavior"> en xml y luego en el código haz lo siguiente. // obtener la vista de la hoja inferior LinearLayout llBottomSheet = (LinearLayout) findViewById (R.id.bottom_sheet); // iniciar el comportamiento de la hoja inferior BottomSheetBehavior bottomSheetBehavior = BottomSheetBehavior.from (llBottomSheet);
Jamal
1

Prueba esto.

1) Cree la hoja inferior y declare la variable en su clase java como

private BottomSheetBehavior sheetBehavior;

2) sheetBehavior = BottomSheetBehavior.from(bottomSheet);

3) En la función de devolución de llamada de la hoja inferior, agregue las siguientes líneas.

sheetBehavior.setBottomSheetCallback(new BottomSheetBehavior.BottomSheetCallback() {
            @Override
            public void onStateChanged(@NonNull View bottomSheet, int newState) {
                switch (newState) {
                    case BottomSheetBehavior.STATE_HIDDEN:
                        Log.d(TAG, "--------------  STATE_HIDDEN");
                        break;
                    case BottomSheetBehavior.STATE_EXPANDED: {
                        Log.d(TAG, "--------------  STATE_EXPANDED");
                    }
                    break;
                    case BottomSheetBehavior.STATE_COLLAPSED: {
                        Log.d(TAG, "--------------  STATE_COLLAPSED");
                    }
                    break;
                    case BottomSheetBehavior.STATE_DRAGGING:
                        sheetBehavior.setState(BottomSheetBehavior.STATE_EXPANDED);
                        break;
                    case BottomSheetBehavior.STATE_SETTLING:
                        Log.d(TAG, "--------------  STATE_SETTLING");
                        break;
                }
            }

            @Override
            public void onSlide(@NonNull View bottomSheet, float slideOffset) {

            }
        });
Surendar D
fuente
1

Al principio, solo quiero dar las gracias a todos los que intentaron dar una respuesta. Solo estoy escribiendo esta respuesta resolviendo este problema como quiero. Voy a describir cómo lo hago paso a paso tomando ayuda desde aquí.

Visualización: Después de hacer clic en el botón Show BottomSheet, verá la segunda pantalla . Ahora verá que BottomSheet está bloqueado para arrastrar . Pero si hace clic en la lista de países, la hoja inferior se ocultará. Esta fue la descripción. Ahora profundicemos en el Código.

  • Al principio, agregue la biblioteca de soporte de diseño a su archivo build.gradle :

    implementación 'com.android.support:design:28.0.0'

  • UserLockBottomSheetBehavior.java : Crédito: James Davis (Gracias hombre)

public class UserLockBottomSheetBehavior<V extends View> extends BottomSheetBehavior<V> {

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

}
  • bottomsheet.xml
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout
    xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:app="http://schemas.android.com/apk/res-auto"
    android:id="@+id/bottomSheet"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    android:layout_gravity="center_vertical"
    android:orientation="vertical"
    app:behavior_hideable="true"
    app:layout_behavior="com.samsolution.custombottomsheet.UserLockBottomSheetBehavior">

 <RelativeLayout
     android:id="@+id/minimizeLayout"
     android:background="@color/colorPrimary"
     android:layout_width="match_parent"
     android:layout_height="?android:attr/actionBarSize">

     <TextView
         android:layout_centerHorizontal="true"
         android:padding="16dp"
         android:layout_width="wrap_content"
         android:layout_height="?android:attr/actionBarSize"
         android:gravity="center_horizontal|center"
         android:text="Country List"
         android:textColor="#FFFFFF"
         android:textStyle="bold" />
 </RelativeLayout>

    <android.support.v7.widget.CardView
        android:layout_width="match_parent"
        android:layout_height="match_parent">

        <ListView
            android:id="@+id/homeCountryList"
            android:layout_width="match_parent"
            android:layout_height="match_parent" />

    </android.support.v7.widget.CardView>

</LinearLayout>
  • activity_main.xml
<?xml version="1.0" encoding="utf-8"?>
<android.support.design.widget.CoordinatorLayout xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:tools="http://schemas.android.com/tools"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    android:background="#FFFFFF"
    tools:context=".MainActivity">

    <LinearLayout
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:orientation="vertical"
        android:layout_gravity="center"
        android:onClick="showCountryListFromBottomSheet">

        <Button
            android:layout_gravity="center"
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            android:background="@android:color/holo_red_light"
            android:onClick="showCountryListFromBottomSheet"
            android:padding="16dp"
            android:text="Show BottomSheet"
            android:textAllCaps="false"
            android:textColor="#ffffff" />

    </LinearLayout>

    <include layout="@layout/bootomsheet" />

</android.support.design.widget.CoordinatorLayout>
  • MainActivity.java
public class MainActivity extends AppCompatActivity {

    private BottomSheetBehavior<LinearLayout> bottomSheetBehavior;                                  // BottomSheet Instance
    LinearLayout bottomsheetlayout;
    String[] list = {"A", "B", "C", "D", "A", "B", "C", "D","A", "B", "C", "D","A", "B", "C", "D","A", "B", "C", "D"};

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);

        bottomsheetlayout = findViewById(R.id.bottomSheet);
        bottomSheetBehavior = BottomSheetBehavior.from(bottomsheetlayout);

        ListView listView = findViewById(R.id.homeCountryList);
        ArrayAdapter<String> adapter = new ArrayAdapter<>(this,android.R.layout.simple_list_item_1,list);
        listView.setAdapter(adapter);

        bottomSheetHide();                                                                          //BottomSheet get hide first time

        RelativeLayout minimizeLayoutIV;                                                            // It will hide the bottomSheet Layout
        minimizeLayoutIV = findViewById(R.id.minimizeLayout);
        minimizeLayoutIV.setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View v) {
               bottomSheetHide();
            }
        });
    }

    public void showCountryListFromBottomSheet(View view) {
        bottomSheetBehavior.setHideable(false);
        bottomSheetBehavior.setState(BottomSheetBehavior.STATE_EXPANDED);
    }

    public void bottomSheetHide(){
        bottomSheetBehavior.setHideable(true);
        bottomSheetBehavior.setState(BottomSheetBehavior.STATE_HIDDEN);
    }
}

Primera pantalla Segunda pantalla

Shahadat Hossain
fuente
1

La solución de la respuesta aceptada funcionó principalmente para mí, pero con un problema: las vistas, que están detrás de la vista de la hoja inferior, comenzaron a reaccionar en los eventos táctiles, si el evento táctil ocurre en el área de la hoja inferior, que está libre de vistas secundarias. En otras palabras, como puede ver en la imagen a continuación, cuando el usuario desliza el dedo dentro de la hoja inferior, el mapa comienza a reaccionar.

área táctil de la hoja inferior

Para solucionar el problema, modifiqué el onInterceptTouchEventmétodo estableciendo touchListeneren la vista de hoja inferior (el resto del código sigue siendo el mismo que en la solución aceptada).

override fun onInterceptTouchEvent(
        parent: CoordinatorLayout,
        child: V, event: MotionEvent
    ): Boolean {
        child.setOnTouchListener { v, event ->
            true
        }
        return false
    }
Myroslav Kolodii
fuente
1

Con 'com.google.android.material:material:1.2.0-alpha06'

Funciona muy bien con NestedScrollView yRecyclerView

Código de ejemplo:

    LinearLayout contentLayout = findViewById(R.id.contentLayout);
    sheetBehavior = BottomSheetBehavior.from(contentLayout);
    sheetBehavior.setDraggable(false);
F.Mysir
fuente
0

Ajustar el peakHeightvalor funcionó para mí.

Configuré la altura del pico como la altura de la hoja inferior si está expandida.

    private val bottomSheetCallback = object : BottomSheetBehavior.BottomSheetCallback() {
    override fun onSlide(bottomSheet: View, slideOffset: Float) {

    }

    override fun onStateChanged(bottomSheet: View, newState: Int) {
        if (newState == BottomSheetBehavior.STATE_EXPANDED)
            bottomSheetBehavior.peekHeight = bottomSheet.height
    }
}
pz64_
fuente
1
Esto no es ideal ya que puede causar animaciones inesperadas.
Adam Hurwitz
En mi caso. No causó ningún problema con las animaciones. Simplemente no se mueve después de que se expande la carta. No es ideal, ¡pero funcionó como se esperaba!
pz64_
Interesante, ese podría ser el caso. Resolví el problema con el cierre inesperado de mi hoja inferior estableciendo la barra de herramientas CollapsingToolbarLayout en Invisible o Gone cuando la hoja inferior está abierta. Una interacción táctil relacionada con la barra de herramientas a pesar de que estaba debajo estaba causando que la hoja inferior se cerrara inesperadamente. El problema está solucionado ahora.
Adam Hurwitz
0
    LayoutInflater inflater = LayoutInflater.from(context);
            View view = inflater.inflate(R.layout.bottomsheet_view_profile_image, null);
            BottomSheetDialog dialog = new BottomSheetDialog(context);
            dialog.setContentView(view);
            dialog.setCancelable(false);


            BottomSheetBehavior behavior = BottomSheetBehavior
                    .from(((View) view.getParent()));
            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) {
                }
            });
            behavior.setState(BottomSheetBehavior.STATE_EXPANDED);
            behavior.setSkipCollapsed(true);
            dialog.show();
SRBhagwat
fuente
0

este es el primer resultado en google, así que creo que es justo poner la solución simple aquí:

   private fun disableDragToDismiss() {
    if (dialog is BottomSheetDialog) {
        val bsDialog = dialog as BottomSheetDialog
        bsDialog.behavior.isHideable = false
    } else {
        Log.d(TAG, " BottomSheetDialog with dialog that is not BottomSheetDialog")
    }
}

y que simplemente llamarlo desde onCreateView()en la BottomSheetDialogFragmentimplementación

Yosef
fuente
0

Tengo el mismo problema BottomSheetDialogFragmenty aplico muchas soluciones usando el behaviorde dialogpero ninguno de ellos resuelve mi problema y luego lo resolví pero configuré setCancelable(false);en el momento de la inicialización de dialog.

DialogEndRide dialogCompleteRide = new DialogEndRide();
dialogCompleteRide.setCancelable(false);
dialogCompleteRide.show(getChildFragmentManager(), "");

Esto desactivará el gesto BottomSheetDialogFragmenty puede descartarlo dialogmediante programación usando la dismiss();función.

rana_sadam
fuente
-1

Tuve el mismo problema, lo resolví por código. No permitirá que el usuario arrastre la hoja inferior. necesita manejar el estado mediante programación.

 mBottomSheetBehavior.isDraggable = false
Pritesh Vishwakarma
fuente
-2

Simplemente use: bottomSheet.dismissOnDraggingDownSheet = false

Copiado del sitio web Material.io:

let viewController: ViewController = ViewController() let bottomSheet: MDCBottomSheetController = MDCBottomSheetController(contentViewController: viewController)

// **** Esta línea evita descartar arrastrando bottomSheet ****

bottomSheet.dismissOnDraggingDownSheet = false

present(bottomSheet, animated: true, completion: nil)

Henry Tokin
fuente
es para iOS aquí, no para Android
Back Packer