Désactivation du glissement de l'utilisateur sur la feuille inférieure


99

J'essaie de désactiver le glissement de l'utilisateur BottomSheet. La raison pour laquelle je veux désactiver est deux choses. 1. Cela empêche le ListViewde défiler vers le bas, 2. Je ne veux pas que les utilisateurs abandonnent en utilisant le glissement mais avec un bouton sur le BottomSheetView. C'est ce que j'ai fait

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

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


S'il vous plaît, vérifiez ma réponse. J'ai remarqué que c'est plus pertinent que la réponse acceptée
Vitalii Obideiko

Réponses:


92

Cela peut maintenant ne plus être pertinent, mais je vais le laisser ici:

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

Et utilisez-le dans votre fichier xml:

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

Il désactive toutes les actions des utilisateurs, il peut être utilisé lorsque vous souhaitez contrôler BottomSheet uniquement par programme.


2
C'est la meilleure réponse pour désactiver BottomSheetBehaviour. Un homme ci-dessus a également publié une solution similaire, mais il n'a pas écrit pour remplacer d'autres événements comme onTouchEvent () . D'autre part, vous pouvez améliorer votre réponse si vous mettez un drapeau au lieu de faux
murt

3
Comment utilisez-vous cela avec un BottomSheetFragment?
user3144836

7
Vous devez spécifiquement faire référence à cette classe dans votre XML. app: layout_behavior = "com.my.package.UserLockBottomSheetBehavior"
Steve

3
Dans certains cas, cela ne fonctionne toujours pas, si nous avons une liste dans le fragment de feuille du bas, cela traîne toujours
Deepak Joshi

1
@DeepakJoshi peut-être que vous pouvez étendre RecyclerView et remplacer quelques méthodes comme 'hasNestedScrollingParent', mais je ne suis pas sûr
Vitalii Obideiko

74

vérifier l'état dans la onStateChangedméthode setBottomSheetCallbacksi l'état est BottomSheetBehavior.STATE_DRAGGINGalors changez-le de BottomSheetBehavior.STATE_EXPANDEDcette façon, vous pouvez arrêter STATE_DRAGGINGpar l'utilisateur. comme ci-dessous

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

utilisez le bouton pour ouvrir la feuille inférieure comme ci-dessous

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

ne pas utiliser setPeekHeightouapp:behavior_peekHeight

par dessus, vous pouvez atteindre votre objectif


1
Joli tour. Je n'ai pas remarqué cela. Merci. Et aussi, pouvez-vous nous aider. Quand je lui dis de se développer au début, c'est transparent et je peux voir la vue derrière, mais je ne peux pas interagir tant que je n'ai pas appuyé sur EditText dans la SheetView avant de le rendre visible.
Tonespy

J'ai fait mon BottomSheet View match_parentet chaque fois que j'essaie de le faire apparaître dans mon, Activityj'ai remarqué qu'il glissait vers le haut, mais ce n'est pas visible tant que je n'ai pas EditTextKeyboardBottomSheet View
tapé

1
J'ai essayé cela, mais les états finissent par STATE_SETTLING. J'ai un bouton pour ouvrir et fermer la feuille du bas, si elle est CACHÉE, je la développe. S'il est EXPANSÉ, je le cache. Comme il reste bloqué dans SETTLING, mon bouton ne fonctionne pas après avoir fait glisser la feuille du bas. Une idée là-dessus?
Gokhan Arik

3
Cette solution n'est pas fiable; la feuille du bas entre dans un mauvais état, comme Gokhan l'a dit ... et quand dans ce mauvais état, des appels comme le chargement d'un nouveau fragment dans la feuille du bas seront simplement vides.
Ray W

7
Cela ne fonctionnera pas si vous avez nestedscrollview à l'intérieur de la feuille du bas
Rishabh Chandel

32

D'accord, donc la réponse acceptée n'a pas fonctionné pour moi. Cependant, la réponse de Виталий Обидейко inspiré ma solution finale.

Tout d'abord, j'ai créé le BottomSheetBehavior personnalisé suivant. Il remplace toutes les méthodes impliquant le toucher et renvoie false (ou n'a rien fait) s'il est verrouillé. Sinon, il agit comme un BottomSheetBehavior normal. Cela désactive la capacité de l'utilisateur à faire glisser vers le bas et n'affecte pas la modification de l'état dans le code.

LockableBottomSheetBehavior.java

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

    private boolean mLocked = false;

    public LockableBottomSheetBehavior() {}

    public LockableBottomSheetBehavior(Context context, AttributeSet attrs) {
        super(context, attrs);
    }

    public void setLocked(boolean locked) {
        mLocked = locked;
    }

    @Override
    public boolean onInterceptTouchEvent(CoordinatorLayout parent, V child, MotionEvent event) {
        boolean handled = false;

        if (!mLocked) {
            handled = super.onInterceptTouchEvent(parent, child, event);
        }

        return handled;
    }

    @Override
    public boolean onTouchEvent(CoordinatorLayout parent, V child, MotionEvent event) {
        boolean handled = false;

        if (!mLocked) {
            handled = super.onTouchEvent(parent, child, event);
        }

        return handled;
    }

    @Override
    public boolean onStartNestedScroll(CoordinatorLayout coordinatorLayout, V child, View directTargetChild, View target, int nestedScrollAxes) {
        boolean handled = false;

        if (!mLocked) {
            handled = super.onStartNestedScroll(coordinatorLayout, child, directTargetChild, target, nestedScrollAxes);
        }

        return handled;
    }

    @Override
    public void onNestedPreScroll(CoordinatorLayout coordinatorLayout, V child, View target, int dx, int dy, int[] consumed) {
        if (!mLocked) {
            super.onNestedPreScroll(coordinatorLayout, child, target, dx, dy, consumed);
        }
    }

    @Override
    public void onStopNestedScroll(CoordinatorLayout coordinatorLayout, V child, View target) {
        if (!mLocked) {
            super.onStopNestedScroll(coordinatorLayout, child, target);
        }
    }

    @Override
    public boolean onNestedPreFling(CoordinatorLayout coordinatorLayout, V child, View target, float velocityX, float velocityY) {
        boolean handled = false;

        if (!mLocked) {
            handled = super.onNestedPreFling(coordinatorLayout, child, target, velocityX, velocityY);
        }

        return handled;

    }
}

Voici un exemple de son utilisation. Dans mon cas, j'en avais besoin pour que la feuille inférieure soit verrouillée une fois développée.

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

J'espère que cela aidera à dissiper une grande partie de la confusion!


1
Bien, c'est la meilleure réponse que nous pouvons éviter de contourner ces états qui conduisent à manquer des événements. Je vous remercie.
Tấn Nguyên

@James - Bonne réponse mais maintenant je ne suis pas en mesure de setPeekHeight (). Une idée?
Adarsh ​​Yadav

J'ai essayé ça. ça marche pour moi. merci mon frère d'avoir sauvé mon cul
Sup.Ia

1
C'est une bonne solution de contournement, bien qu'elle ne soit pas mise à jour à ce jour. OnNestedPreScroll et certaines autres méthodes sont obsolètes. Besoin de mettre à jour ces méthodes et cela fonctionne très bien.
Ajay le

4
Bonjour, cela ne fonctionne pas sur un BottomSheetDialogFragment, je peux toujours faire glisser la feuille de fond
florian-do

23

J'ai fini par écrire une solution de contournement pour résoudre ce cas d'utilisation de désactivation dynamique du glissement utilisateur, dans lequel BottomSheetBehavior est sous-classé pour remplacer onInterceptTouchEvent, et pour l'ignorer lorsqu'un indicateur personnalisé (dans ce cas, mAllowUserDragging) est défini sur 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);
    }
}

Et dans votre mise en page 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" />

Jusqu'à présent, il s'agit de la solution la plus cohérente pour désactiver le glissement utilisateur sur la feuille du bas à la demande.

Toutes les autres solutions qui reposaient sur le déclenchement d'un autre appel setState dans le rappel onStateChanged ont abouti à un mauvais état de BottomSheet ou à des problèmes UX importants (dans le cas de la publication de l'appel setState dans un Runnable).

J'espère que cela aide quelqu'un :)

Rayon


4
C'est plutôt chouette
Odys

3
@BeeingJk Au lieu de FrameLayout, utilisez NestedScrollView et définissezbottomSheetFragContainer.setNestedScrollingEnabled(false);
AfzalivE

1
RÉSOLU: en définissant le rappel de CoordinatorLayout.Behavior behavior = layoutParams.getBehavior (); if (behavior! = null && behavior instanceof BottomSheetBehavior) {((BottomSheetBehavior) behavior) .setBottomSheetCallback (mBottomSheetBehaviorCallback); }
LOG_TAG

3
Cela ne me plaît pas! PS: J'ai un texte déroulant dans la feuille de fond
Thorvald Olavsen

6
Comment le castez-vous lors de l'initialisation? Cela me donne un avertissement WABottomSheetBehavior <View> behavior = (WABottomSheetBehavior) BottomSheetBehavior.from (sheetView);
Leo Droidcoder du

8

Réponse tardive, mais c'est ce qui a fonctionné pour moi, ce qui est un peu différent de ce que d'autres ont suggéré.

Vous pouvez essayer de définir la cancelablepropriété sur false, c'est-à-dire

setCancelable(false);

puis gérer manuellement les événements pour lesquels vous souhaitez fermer la boîte de dialogue dans la setupDialogméthode.

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

Cela fonctionne avec un ListView à l'intérieur du fragment de boîte de dialogue, où je me retrouvais un peu coincé avec d'autres solutions.


Belle solution concise. À toute personne lisant ceci, vous voudrez (probablement) des vérifications supplémentaires pour event.isCanceled()et event.getAction() == MotionEvent.ACTION_UPavant de fermer la boîte de dialogue - cela empêchera les faux-clics de déclencher le rejet.
Eric Bachhuber

Merci pour cela. C'est la solution la plus simple pour désactiver le glissement.
AVJ

7

La réponse acceptée ne fonctionne pas sur le premier appareil de test que j'utilise. Et le rebond n'est pas fluide. Il semble préférable de définir l'état sur STATE_EXPANDED uniquement après qu'un utilisateur a relâché le glissement. Voici ma version:

    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
Laissez-moi vous dire le problème avec le jeter dans un exécutable à moins que ce ne soit ce que vous voulez. Vous ne pouvez pas le supprimer avec un bouton car il doit faire glisser pour le supprimer. Et, il répondra toujours au glissement, juste que cela empêcherait l'utilisateur de faire glisser pour
ignorer

7

Ajoutez ce code à l' objet BottomSheetBehavior . Le glissement sera désactivé. Fonctionne bien pour moi.

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
Cela ne désactive pas le balayage. Il réduit complètement la feuille inférieure.
Adam Hurwitz

7

Comportement prévisible:

  • BottomSheet ne se ferme pas lors du glisser vers le bas
  • BottomSheet se ferme si elle est touchée en dehors de la fenêtre de dialogue

Code:

class MyBottomSheet : BottomSheetDialogFragment () {

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

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

 }

Pour une raison quelconque, je ne peux pas fermer la boîte de dialogue en touchant l'extérieur, mais cela fonctionne pour désactiver la traînée
Gastón Saillén

5

Pour verrouiller le BottomSheet et éviter à l'utilisateur de le faire glisser, c'est ce que j'ai fait

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

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

Cela fonctionne assez bien pour moi.


Cette solution était attrayante mais fait bizarrement apparaître la feuille du bas du haut de l'écran au lieu du bas! Il disparaît cependant de manière normale. C'est très Star Trek.
Tunga

J'avais besoin de faire une modification de vue et de l'utiliser à la place BottomSheetBehavior.STATE_HIDDEN. Dans ce cas, vous ne devez pas non plus appeler setPeekHeight(). C'est beaucoup moins compliqué que d'autres solutions ici.
HolySamosa

4

Un moyen facile de verrouiller le glissement est de définirPeekHeight comme la hauteur de la vue. Par exemple:

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 exemple avec BottomSheetDialogFragment. Cela fonctionne parfaitement.

Edit 09/04/2020: Remplacé amorti setBottomSheetCallback()paraddBottomSheetCallback()

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

Vous n'avez pas besoin de bloquer tous les événements lorsque la feuille du bas est désactivée. Vous ne pouvez bloquer que l'événement ACTION_MOVE. C'est pourquoi utiliser un comportement de feuille de fond personnalisé comme celui-ci

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

Comment utilisez-vous cette classe? Je reçois une exception IllegalArgumentException: la vue n'est pas associée à BottomSheetBehavior
user3144836

3

Voici une version fonctionnelle de la meilleure solution de 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
    }
}

Puis chaque fois que vous voulez utiliser:

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

Il bottom_sheet_mains'agit de la vue réelle utilisant les extensions Android Kotlin .


3

définissez bottomSheet onClickListener sur null.

bottomSheet.setOnClickListener(null);

cette ligne désactive toutes les actions sur bottomSheet uniquement et n'affecte pas la vue intérieure.


1
Cela provoque une animation inattendue lorsque la feuille inférieure tente de se fermer.
Adam Hurwitz

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

vous pouvez désactiver le glissement du BottomSheet comme ceci.

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), j'espère que cette réponse pourra résoudre votre problème.


1
Cette version alpha se comporte de manière folle. Je ne recommande pas :(
Adam Styrc

2

J'ai trouvé une solution incroyable. Le problème initial était qu'une fois que bottomSheet passait à l'état HIDDEN, il ne s'affichait pas à bottomSheetDialog.show (). Mais je voulais que la boîte de dialogue soit visible sur la méthode show () et je voulais également permettre à l'utilisateur de la faire glisser vers le bas pour qu'elle ressemble à la feuille du bas. Voici ce que j'ai fait.

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

    }
};

celui-ci est une réponse parfaite
Vivek Kumar Srivastava

2
  1. Copie BottomSheetDialog dans votre projet et renommez-le enMyBottomSheetDialog
  2. ajouter getBottomSheetBehavior àMyBottomSheetDialog
  3. utilisation MyBottomSheetDialog placeBottomSheetDialog
  4. bottomSheetBehavior.setBottomSheetCallback

code comme ça

public class MyBottomSheetDialog extends AppCompatDialog {

    // some code

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

    // more code

dans votre code

    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

Ceci est essentiellement la version kotlin de la bonne réponse en haut:

    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
}

Comment utilisez-vous cette classe? Je reçois une exception IllegalArgumentException: la vue n'est pas associée à BottomSheetBehavior
user3144836

1
app: layout_behavior = "UserLockBottomSheetBehavior"> en xml, puis dans le code, procédez comme suit. // récupère la vue de la feuille inférieure LinearLayout llBottomSheet = (LinearLayout) findViewById (R.id.bottom_sheet); // initie le comportement de la feuille inférieure BottomSheetBehavior bottomSheetBehavior = BottomSheetBehavior.from (llBottomSheet);
Jamal

1

Essaye ça.

1) Créez la feuille du bas et déclarez la variable dans votre classe java comme

private BottomSheetBehavior sheetBehavior;

2) sheetBehavior = BottomSheetBehavior.from(bottomSheet);

3) Dans la fonction de rappel de la feuille du bas, ajoutez les lignes suivantes.

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

Au début, je veux juste remercier tous ceux qui ont essayé de donner une réponse. J'écris juste cette réponse en résolvant ce problème comme je le souhaite. Je vais décrire comment je fais cela étape par étape en prenant de l'aide d'ici.

Visualisation: Après avoir cliqué sur le bouton, Show BottomSheetvous verrez le deuxième écran . Vous verrez maintenant que BottomSheet est juste verrouillé pour le glisser . Mais si vous cliquez sur la liste des pays, la feuille du bas se cachera. C'était la description maintenant, creusons dans le code.

  • Dans un premier temps, ajoutez la bibliothèque de support de conception à votre fichier build.gradle :

    implémentation 'com.android.support:design:28.0.0'

  • UserLockBottomSheetBehavior.java : Crédit: James Davis (Thanks Man)

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

Premier écran Deuxième écran


1

La solution de la réponse acceptée a principalement fonctionné pour moi, mais avec un problème: les vues, qui se trouvent derrière la vue inférieure de la feuille, ont commencé à réagir aux événements tactiles, si l'événement tactile se produit sur la zone de la feuille inférieure, qui est libre de vues enfants. En d'autres termes, comme vous pouvez le voir dans l'image ci-dessous, lorsque l'utilisateur glisse le doigt à l'intérieur de la feuille inférieure, la carte commence à réagir.

zone tactile de la feuille inférieure

Pour résoudre le problème, j'ai modifié la onInterceptTouchEventméthode en définissant la vue touchListenerdu bas de la feuille (le reste du code reste le même que dans la solution acceptée).

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

1

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

Fonctionne très bien avec NestedScrollViewetRecyclerView

Exemple de code:

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

0

Ajuster la peakHeightvaleur a fonctionné pour moi.

J'ai défini la hauteur du pic comme la hauteur de la feuille de fond si elle est développée.

    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
Ce n'est pas idéal car cela peut provoquer des animations inattendues.
Adam Hurwitz

Dans mon cas. Cela n'a causé aucun problème d'animation. Il ne bouge tout simplement pas après l'extension de la carte. Ce n'est pas idéal mais cela a fonctionné comme prévu!
pz64_

Intéressant, cela pourrait être le cas. J'ai résolu le problème de fermeture inattendue de ma feuille inférieure en définissant la barre d'outils de CollapsingToolbarLayout sur Invisible ou Gone lorsque la feuille inférieure est ouverte. Une interaction tactile liée à la barre d'outils même si elle était en dessous provoquait la fermeture inattendue de la feuille inférieure. Le problème est résolu maintenant.
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

c'est le premier résultat dans google, donc je pense que c'est juste de mettre la solution simple ici:

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

et que de simplement l'appeler depuis onCreateView()la BottomSheetDialogFragmentmise en œuvre


0

J'ai le même problème BottomSheetDialogFragmentet j'applique de nombreuses solutions à l'aide de behaviorof dialogmais aucune de celles-ci ne résout mon problème, puis je l'ai résolu en définissant setCancelable(false);au moment de l'initialisation de dialog.

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

Cela désactivera le geste sur BottomSheetDialogFragmentet vous pouvez ignorer dialogpar programme en utilisant la dismiss();fonction.


-1

J'ai eu le même problème, je l'ai résolu par code. Cela ne permettra pas à l'utilisateur de faire glisser la feuille de fond. vous devez gérer l'état par programme.

 mBottomSheetBehavior.isDraggable = false

-2

Utilisez simplement: bottomSheet.dismissOnDraggingDownSheet = false

Copié du site Web Material.io:

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

// **** Cette ligne empêche le rejet en faisant glisser bottomSheet ****

bottomSheet.dismissOnDraggingDownSheet = false

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


c'est pour iOS ici pas pour Android
Back Packer
En utilisant notre site, vous reconnaissez avoir lu et compris notre politique liée aux cookies et notre politique de confidentialité.
Licensed under cc by-sa 3.0 with attribution required.