Disattivazione del trascinamento dell'utente su BottomSheet


99

Sto provando a disabilitare il trascinamento dell'utente BottomSheet. Il motivo per cui voglio disabilitare è due cose. 1. Impedisce a ListViewdi scorrere verso il basso, 2. Non voglio che gli utenti si chiudano usando il trascinamento ma con un pulsante su BottomSheetView. Questo è quello che ho fatto

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

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


Per favore, controlla la mia risposta. Ho notato che è più pertinente della risposta accettata
Vitalii Obideiko

Risposte:


92

Ora può non essere più rilevante, ma lo lascio qui:

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

E usalo nel tuo file xml:

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

Disabilita tutte le azioni degli utenti, può essere utilizzato quando si desidera controllare BottomSheet solo a livello di programmazione.


2
Questa è la risposta migliore per disabilitare BottomSheetBehaviour. Un uomo sopra ha anche pubblicato una soluzione simile, ma non ha scritto per sovrascrivere altri eventi come onTouchEvent () . D'altra parte potresti migliorare la tua risposta se metti una bandiera invece di falso
murt il

3
Come lo usi con un BottomSheetFragment?
user3144836

7
Devi fare riferimento in modo specifico a questa classe nel tuo XML. app: layout_behavior = "com.my.package.UserLockBottomSheetBehavior"
Steve,

3
In alcuni casi, questo ancora non funziona, se abbiamo un elenco nel frammento del foglio inferiore, viene comunque trascinato
Deepak Joshi

1
@DeepakJoshi forse potresti estendere RecyclerView e ignorare alcuni metodi come "hasNestedScrollingParent", ma non ne sono sicuro
Vitalii Obideiko

74

controlla lo stato nel onStateChangedmetodo setBottomSheetCallbackse lo stato è BottomSheetBehavior.STATE_DRAGGINGquindi modificalo in BottomSheetBehavior.STATE_EXPANDEDquesto modo puoi fermarlo STATE_DRAGGINGdall'utente. come sotto

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

utilizzare il pulsante per aprire il foglio inferiore di chiusura come sotto

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

non usare setPeekHeightoapp:behavior_peekHeight

in questo modo puoi raggiungere il tuo obiettivo


1
Bel trucco. Non l'ho notato. Grazie. E inoltre, puoi aiutare con questo. Quando gli dico di espandersi all'inizio, è trasparente e posso vedere la vista dietro, ma non posso interagire finché non tocco l'EditText in SheetView prima di renderlo visibile.
Tonespy

Ho creato il mio BottomSheet View match_parente ogni volta che provo a visualizzarlo nel mio Activityho notato che scivola su, ma non è visibile fino a quando non tocco il EditTextKeyboardBottomSheet View
simbolo

1
Ho provato questo, ma gli stati finiscono in STATE_SETTLING. Ho un pulsante per aprire e chiudere il foglio inferiore, se è NASCOSTO, lo espando. Se è ESPANSO, lo nascondo. Poiché si blocca in SETTLING, il mio pulsante non funziona dopo aver trascinato il foglio inferiore. Qualche idea su questo?
Gokhan Arik

3
Questa soluzione è inaffidabile; il foglio inferiore entra in uno stato cattivo, come ha detto Gokhan ... e quando in quello stato cattivo, chiamate come il caricamento di un nuovo frammento nel foglio inferiore verranno semplicemente cancellate.
Ray W

7
Non funzionerà se hai nestedscrollview all'interno del foglio inferiore
Rishabh Chandel

32

Va bene, quindi la risposta accettata non ha funzionato per me. Tuttavia, la risposta di Виталий Обидейко ha ispirato la mia soluzione finale.

Innanzitutto, ho creato il seguente BottomSheetBehavior personalizzato. Ignora tutti i metodi che coinvolgono il tocco e restituisce false (o non ha fatto nulla) se è bloccato. Altrimenti, si comporta come un normale BottomSheetBehavior. Ciò disabilita la capacità dell'utente di trascinare verso il basso e non influisce sulla modifica dello stato nel codice.

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;

    }
}

Ecco un esempio di come usarlo. Nel mio caso, ne avevo bisogno in modo che il foglio inferiore fosse bloccato quando espanso.

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

Spero che questo aiuti a chiarire gran parte della confusione!


1
Bello, è la migliore risposta che possiamo evitare di aggirare questi stati che portano a perdere eventi. Grazie.
Tấn Nguyên

@ James - Bella risposta ma ora non sono in grado di impostarePeekHeight (). Qualche idea?
Adarsh ​​Yadav,

Ho provato questo. per me funziona. grazie fratello per
avermi

1
Questa è una buona soluzione alternativa, sebbene non sia aggiornata ad oggi. OnNestedPreScroll e alcuni altri metodi sono stati deprecati. È necessario aggiornare questi metodi e funziona perfettamente.
Ajay

4
Ciao, non funziona su BottomSheetDialogFragment, posso comunque trascinare il foglio inferiore
florian-do

23

Ho finito per scrivere una soluzione alternativa per affrontare questo caso d'uso di disabilitazione dinamica del trascinamento dell'utente, per cui BottomSheetBehavior è sottoclasse per sovrascrivere suInterceptTouchEvent e per ignorarlo quando un flag personalizzato (in questo caso mAllowUserDragging) è impostato su false:

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

E nel tuo layout 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" />

Finora, questa è la soluzione con il comportamento più coerente per disabilitare il trascinamento dell'utente sul foglio inferiore su richiesta.

Tutte le altre soluzioni che si basavano sull'attivazione di un'altra chiamata setState nel callback onStateChanged hanno portato il BottomSheet in uno stato errato o hanno causato problemi significativi di UX (nel caso di pubblicazione della chiamata setState in un Runnable).

Spero che questo aiuti qualcuno :)

Ray


4
È abbastanza carino
Odys

3
@BeeingJk Invece di FrameLayout, usa NestedScrollView e impostabottomSheetFragContainer.setNestedScrollingEnabled(false);
AfzalivE

1
RISOLTO: impostando il callback CoordinatorLayout.Behavior behavior = layoutParams.getBehavior (); if (behavior! = null && behavior instanceof BottomSheetBehavior) {((BottomSheetBehavior) behavior) .setBottomSheetCallback (mBottomSheetBehaviorCallback); }
LOG_TAG

3
Questo non funziona per me! PS: ho un testo scorrevole nel foglio inferiore
Thorvald Olavsen

6
Come lo lanci durante l'inizializzazione? Questo mi dà un avviso WABottomSheetBehavior <View> behavior = (WABottomSheetBehavior) BottomSheetBehavior.from (sheetView);
Leo Droidcoder

8

Risposta tardiva, ma questo è ciò che ha funzionato per me, che è un po 'diverso da quello che altri hanno suggerito.

Potresti provare a impostare la cancelableproprietà su false, ad es

setCancelable(false);

e quindi gestire manualmente gli eventi in cui si desidera chiudere la finestra di dialogo nel setupDialogmetodo.

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

Funziona con un ListView all'interno del frammento di dialogo, che era il punto in cui mi trovavo un po 'bloccato con altre soluzioni.


Bella soluzione concisa. Per chiunque legga questo, (probabilmente) vorrai controlli aggiuntivi per event.isCanceled()e event.getAction() == MotionEvent.ACTION_UPprima di chiudere la finestra di dialogo: questo impedirà ai clic errati di attivare il licenziamento.
Eric Bachhuber

Grazie per questo. Questa è la soluzione più semplice per disabilitare il trascinamento.
AVJ

7

La risposta accettata non funziona sul primo dispositivo di prova che utilizzo. E il rimbalzo non è regolare. Sembra meglio impostare lo stato su STATE_EXPANDED solo dopo che un utente rilascia il trascinamento. Quella che segue è la mia versione:

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

1
Lascia che ti dica il problema di lanciarlo in un runnable a meno che non sia quello che vuoi. Non puoi chiuderlo con un pulsante perché deve essere trascinato per chiuderlo. E risponderà sempre al trascinamento, solo che impedirebbe all'utente di trascinare per chiudere
Tonespy

7

Aggiungi questo codice all'oggetto BottomSheetBehavior . Il trascinamento sarà disabilitato. Funziona bene per me.

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

      }
});

1
Questo non disabilita lo scorrimento. Fa crollare completamente il foglio inferiore.
Adam Hurwitz

7

Comportamento atteso:

  • BottomSheet non si chiude durante il trascinamento
  • BottomSheet si chiude se viene toccato al di fuori della finestra di dialogo

Codice:

class MyBottomSheet : BottomSheetDialogFragment () {

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

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

 }

Per qualche motivo, non riesco a chiudere la finestra di dialogo toccando l'esterno, ma funziona per disabilitare il trascinamento
Gastón Saillén

5

Per bloccare il BottomSheet ed evitare che l'utente lo faccia scorrere, questo è quello che ho fatto

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

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

Funziona abbastanza bene per me.


Questa soluzione era allettante ma stranamente fa apparire il foglio inferiore dalla parte superiore dello schermo anziché dal basso! Tuttavia, scompare normalmente. È molto Star Trek.
Tunga

Avevo bisogno di apportare una modifica alla vista e invece di usare BottomSheetBehavior.STATE_HIDDEN. In tal caso, anche tu non devi chiamare setPeekHeight(). Questo è molto meno complicato di altre soluzioni qui.
HolySamosa

4

Un modo semplice per bloccare il trascinamento è setPeekHeight uguale all'altezza della vista. Per esempio:

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

4

Un esempio con BottomSheetDialogFragment. Funziona perfettamente.

Modifica 09/04/2020: sostituito ammortizzato 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
    }

    ...
}

3

Non è necessario bloccare tutti gli eventi quando il foglio inferiore è disabilitato. Puoi bloccare solo l'evento ACTION_MOVE. Ecco perché utilizzare un comportamento del foglio inferiore personalizzato come questo

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

Come usi questa lezione? Ricevo un'eccezione IllegalArgumentException: la vista non è associata a BottomSheetBehavior
user3144836

3

Ecco una versione funzionante della migliore soluzione in 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
    }
}

Quindi ogni volta che vuoi usare:

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

Questa bottom_sheet_mainè la visualizzazione effettiva utilizzando le estensioni Android di Kotlin .


3

imposta bottomSheet onClickListener su null.

bottomSheet.setOnClickListener(null);

questa linea disabilita tutte le azioni solo su bottomSheet e non ha effetto sulla vista interna.


1
Ciò causa un'animazione imprevista quando il foglio inferiore tenta di chiudersi.
Adam Hurwitz

3
implementation 'com.google.android.material:material:1.2.0-alpha05'

puoi disabilitare il trascinamento del BottomSheet in questo modo.

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), spero che questa risposta possa risolvere il tuo problema.


1
Queste versioni alfa si comportano in modo folle. Non consiglio :(
Adam Styrc il

2

Ho trovato una soluzione straordinaria. Il problema iniziale era che una volta bottomSheet stava andando nello stato NASCOSTO, quindi non veniva visualizzato in bottomSheetDialog.show (). Ma volevo che la finestra di dialogo fosse visibile sul metodo show () e volevo anche consentire all'utente di scorrerla verso il basso in modo che sembrasse il foglio inferiore. Di seguito è quello che ho fatto ..

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

    }
};

questa è una risposta perfetta
Vivek Kumar Srivastava

2
  1. copia BottomSheetDialog nel tuo progetto e rinominalo inMyBottomSheetDialog
  2. aggiungi getBottomSheetBehavioraMyBottomSheetDialog
  3. usa MyBottomSheetDialoginveceBottomSheetDialog
  4. bottomSheetBehavior.setBottomSheetCallback

codice come questo

public class MyBottomSheetDialog extends AppCompatDialog {

    // some code

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

    // more code

nel codice

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

        }

2

Questa è fondamentalmente la versione kotlin della risposta giusta in alto:

    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
}

Come usi questa lezione? Ricevo un'eccezione IllegalArgumentException: la vista non è associata a BottomSheetBehavior
user3144836

1
app: layout_behavior = "UserLockBottomSheetBehavior"> in xml e poi nel codice fai quanto segue. // ottiene la vista del foglio inferiore LinearLayout llBottomSheet = (LinearLayout) findViewById (R.id.bottom_sheet); // avvia il comportamento del foglio inferiore BottomSheetBehavior bottomSheetBehavior = BottomSheetBehavior.from (llBottomSheet);
Jamal

1

Prova questo.

1) Crea il foglio inferiore e dichiara la variabile nella tua classe java come

private BottomSheetBehavior sheetBehavior;

2) sheetBehavior = BottomSheetBehavior.from(bottomSheet);

3) Nella funzione di callback del foglio inferiore aggiungere le seguenti righe.

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

            }
        });

1

All'inizio, voglio solo ringraziare tutti voi che avete cercato di dare una risposta. Sto solo scrivendo questa risposta risolvendo questo problema come voglio. Descriverò come lo faccio passo dopo passo ricevendo aiuto da qui.

Visualizzazione: dopo aver cliccato sul pulsante Show BottomSheetvedrai la seconda schermata . Ora vedrai che BottomSheet è solo bloccato per il trascinamento . Ma se fai clic sull'elenco dei paesi, il foglio inferiore verrà nascosto. Questa era la descrizione, ora approfondiamo il codice.

  • Per prima cosa, aggiungi la libreria di supporto alla progettazione al tuo file build.gradle :

    implementazione "com.android.support:design:28.0.0"

  • UserLockBottomSheetBehavior.java : Credito: James Davis (Grazie uomo)

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

Primo schermo Secondo schermo


1

La soluzione dalla risposta accettata ha funzionato principalmente per me, ma con un problema: le visualizzazioni, che si trovano dietro la visualizzazione del foglio inferiore, hanno iniziato a reagire agli eventi di tocco, se l'evento di tocco si verifica nell'area del foglio inferiore, che è priva di visualizzazioni secondarie. In altre parole, come puoi vedere nell'immagine qui sotto, quando l'utente fa scorrere il dito all'interno del foglio inferiore, la mappa inizia a reagire su di esso.

area di tocco del foglio inferiore

Per risolvere il problema, ho modificato il onInterceptTouchEventmetodo impostando touchListenersulla visualizzazione del foglio inferiore (il resto del codice rimane lo stesso della soluzione accettata).

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

1

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

Funziona alla grande con NestedScrollView eRecyclerView

Codice di esempio:

    LinearLayout contentLayout = findViewById(R.id.contentLayout);
    sheetBehavior = BottomSheetBehavior.from(contentLayout);
    sheetBehavior.setDraggable(false);

0

La regolazione del peakHeightvalore ha funzionato per me.

Ho impostato l'altezza del picco come l'altezza del foglio inferiore se è espanso.

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

1
Questo non è l'ideale in quanto potrebbe causare animazioni impreviste.
Adam Hurwitz

Nel mio caso. Non ha causato alcun problema di animazione. Semplicemente non si muove dopo che la carta è stata espansa. Non è l'ideale ma ha funzionato come previsto!
pz64_

Interessante, potrebbe essere così. Ho risolto il problema con la chiusura imprevista del mio ultimo foglio impostando la barra degli strumenti di CollapsingToolbarLayout su Invisibile o Andato quando il foglio inferiore è aperto. Un'interazione con il tocco relativa alla barra degli strumenti, anche se si trovava sotto, causava la chiusura imprevista del foglio inferiore. Il problema è stato risolto ora.
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();

0

questo è il primo risultato in Google, quindi credo sia giusto inserire qui la soluzione semplice:

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

e poi basta chiamare da onCreateView()nella BottomSheetDialogFragmentrealizzazione


0

Ho lo stesso problema BottomSheetDialogFragmente applico molte soluzioni usando behaviordi, dialogma nessuna di queste risolve il mio problema e poi l'ho risolto ma impostando setCancelable(false);al momento dell'inizializzazione di dialog.

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

Questo disabiliterà il gesto BottomSheetDialogFragmente potrai chiudere in modo dialogprogrammatico usando la dismiss();funzione.


-1

Ho avuto lo stesso problema, l'ho risolto tramite codice. Non consentirà all'utente di trascinare il foglio inferiore. è necessario gestire lo stato a livello di codice.

 mBottomSheetBehavior.isDraggable = false

-2

Usa semplicemente: bottomSheet.dismissOnDraggingDownSheet = false

Copiato dal sito Web di Material.io:

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

// **** Questa riga impedisce l'eliminazione trascinando bottomSheet ****

bottomSheet.dismissOnDraggingDownSheet = false

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


è per iOS qui non per Android
Back Packer
Utilizzando il nostro sito, riconosci di aver letto e compreso le nostre Informativa sui cookie e Informativa sulla privacy.
Licensed under cc by-sa 3.0 with attribution required.