web-dev-qa-db-fra.com

Android: problème d'événement tactile imbriqué de clic / glisser sur la feuille inférieure

J'ai une feuille inférieure imbriquée dans une autre feuille inférieure (FrameLayouts utilisant le comportement de disposition BottomSheet)

J'ai également quelques "vues d'aperçu" (FrameLayouts) auxquelles des écouteurs de clic sont attachés, pour développer respectivement la ou les feuilles inférieures, lorsque vous cliquez dessus.

Ainsi, l'application dispose essentiellement de 3 écrans principaux. Le `` conteneur principal '', puis la première `` feuille inférieure '', qui peut être développée en plein écran, puis au bas de la première feuille inférieure, est la deuxième feuille inférieure, qui peut également être développée en plein écran.

Problème:

Lorsque j'ajoute un RecyclerView à la vue 'conteneur' de la feuille inférieure imbriquée, le glissement cesse de fonctionner pour la deuxième vue d'aperçu (feuille 2 d'aperçu). Si je supprime la vue d'aperçu ClickListener ou la RecyclerView, les choses semblent fonctionner parfaitement bien.

Résultat désiré:

Les deux feuilles inférieures doivent rester déplaçables et les vues d'aperçu doivent pouvoir être cliquées pour développer leur feuille inférieure parent. La feuille inférieure doit répondre aux parchemins imbriqués comme elle le ferait normalement.

J'ai essayé de supprimer le ClickListener et d'utiliser des gestes tactiles à la place, mais rien de ce que j'ai essayé ne semble aider.

J'utilise v25.3.1 de la bibliothèque de support de conception, et je peux reproduire ce problème sur un Galaxy S4 exécutant le stock 4.4.4 et un Nexus 6P exécutant le stock 7.1.2. (Je n'ai pas d'autre appareil disponible).

J'ai également créé un projet de test sur github pour toute personne intéressée à y regarder de plus près: https://github.com/timusus/bottomsheet-test

Voici quelques captures d'écran illustrant la mise en page:

123

La structure de mise en page ressemble à ceci (du code a été omis pour plus de clarté):

<CoordinatorLayout>

    <FrameLayout
        Android:id="@+id/mainContainer" 
        Android:layout_height="match_parent"/>

    <FrameLayout
        Android:id="@+id/sheet1" 
        Android:layout_height="match_parent"
        app:layout_behavior="CustomBottomSheetBehavior"
        app:behavior_peekHeight="64dp">

        <FrameLayout
            Android:id="@+id/sheet1Container"
            Android:layout_height="match_parent"/>

        <CoordinatorLayout>

        <FrameLayout
            Android:id="@+id/sheet2
            Android:layout_height="match_parent"
            app:layout_behavior="CustomBottomSheetBehavior"
            app:behavior_peekHeight="64dp">

            <FrameLayout
                Android:id="@+id/sheet2Container"
                Android:layout_height="match_parent">

                <!-- Problematic RecyclerView -->
                <RecyclerView 
                Android:layout_height="match_parent"/>

            </FrameLayout>

            <!-- Problematic Click Listener on this view -->
            <FrameLayout 
                Android:id="@+id/sheet2PeekView"
                Android:layout_height=64dp"/>

        </FrameLayout>

        </CoordinatorLayout>

        <FrameLayout
            Android:id="@+id/sheet1PeekView"
            Android:layout_height=64dp"/>

    </FrameLayout>
</CoordinatorLayout/>

CustomBottomSheetBehavior n'est qu'une simple sous-classe de BottomSheetBehavior qui empêche la première feuille d'intercepter des événements tactiles si la deuxième feuille est développée ou déplacée. Cela permet à la deuxième feuille d'être glissée de "développé" à "réduit" sans également réduire la première feuille.

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

    private boolean allowDragging = true;

    public void setAllowDragging(boolean allowDragging) {
        this.allowDragging = allowDragging;
    }

    @Override
    public boolean onInterceptTouchEvent(CoordinatorLayout parent, V child, MotionEvent event) {
        if (!allowDragging) {
            return false;
        }

        return super.onInterceptTouchEvent(parent, child, event);
    }
}

Je ne pense pas que la personnalisation de BottomSheetBehavior soit pertinente pour ce problème, mais pour être complet, voici comment elle est utilisée:

FrameLayout sheet1 = (FrameLayout) findViewById(R.id.sheet1);
bottomSheetBehavior1 = (CustomBottomSheetBehavior) BottomSheetBehavior.from(sheet1);

FrameLayout sheet2 = (FrameLayout) findViewById(R.id.sheet2);
       bottomSheetBehavior2 = (CustomBottomSheetBehavior) BottomSheetBehavior.from(sheet2);
       bottomSheetBehavior2.setBottomSheetCallback(new BottomSheetBehavior.BottomSheetCallback() {
           @Override
           public void onStateChanged(@NonNull View bottomSheet, int newState) {
                //If the second sheet is expanded or dragging, don't allow the first sheet to respond to touch events.
               if (newState == BottomSheetBehavior.STATE_EXPANDED || newState == BottomSheetBehavior.STATE_DRAGGING) {
                   bottomSheetBehavior1.setAllowDragging(false);
               } else {
                   bottomSheetBehavior1.setAllowDragging(true);
               }
           }

Je n'arrive pas à comprendre si cela a à voir avec le onInterceptTouchEvent du BottomSheet, la gestion du défilement imbriqué du RecyclerView, View.ClickListener voler des événements tactiles, une combinaison des éléments ci-dessus ou autre chose.

Toute aide serait très appréciée.

10
Tim Malseed

[~ # ~] fixe [~ # ~]

Je n'arrive pas à comprendre si cela a à voir avec onInterceptTouchEvent de la feuille de fond, la gestion du défilement imbriqué de RecyclerView interne, View.ClickListener volant des événements tactiles, une combinaison des éléments ci-dessus ou autre chose.

Il s'agit d'une combinaison des éléments ci-dessus CustomBottomSheetBehavior et View.ClickListener

Le problème était bottomSheetBehavior1 prend un événement de glissement lorsque getSheet2PeekView fait glisser pour détecter le toucher événement sur getSheet2PeekView et définissez bottomSheetBehavior1 en faisant glisser false et bottomSheetBehavior2 true


Solution

Mettez ce code et votre problème est résolu.

findViewById(getSheet2PeekViewResId()).setOnTouchListener(new OnTouchListener() {
        @Override
        public boolean onTouch(View v, MotionEvent event) {
            Log.e(TAG, "onTouch: ");
            bottomSheetBehavior1.setAllowDragging(false);
            bottomSheetBehavior2.setAllowDragging(true);
            return false;
        }
    });

Également créé Pull Request à votre référentiel avec des modifications pleinement fonctionnelles.

14
N J