web-dev-qa-db-fra.com

Utilisez le groupe dans ConstraintLayout pour écouter les événements de clic sur plusieurs vues

En gros, j'aimerais associer un seul OnClickListener à plusieurs vues à l'intérieur d'un ConstraintLayout.

Avant de migrer vers ConstraintLayout, les vues se trouvaient à l’intérieur d’une disposition sur laquelle je pouvais ajouter un écouteur. Ils se trouvent maintenant sur le même calque que d'autres vues, juste sous ConstraintLayout.

J'ai essayé d'ajouter les vues à un Android.support.constraint.Group et de lui ajouter un OnClickListener par programme.

group.setOnClickListener {
    Log.d("OnClick", "groupClickListener triggered")
}

Cependant, cela ne semble pas fonctionner à partir de la version 1.1.0-beta2 de ConstraintLayout

Ai-je commis une erreur, existe-t-il un moyen de résoudre ce problème ou dois-je associer l'auditeur à chacune des vues? 

19
Endzeit

La Group dans ConstraintLayout est simplement une association lâche de vues, autant que je sache. Ce n'est pas une ViewGroup, vous ne pourrez donc pas utiliser un écouteur en un seul clic comme vous le faisiez lorsque les vues étaient dans une ViewGroup.

Au lieu de cela, vous pouvez obtenir une liste des identifiants membres de votre Group dans votre code et définir explicitement l'écouteur de clics. (Je n'ai pas trouvé de documentation officielle sur cette fonctionnalité, mais je pense qu'elle est juste en retard sur la publication du code.) Voir la documentation sur getReferencedIdsici .

Java:

    Group group = findViewById(R.id.group);
    int refIds[] = group.getReferencedIds();
    for (int id : refIds) {
        findViewById(id).setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View view) {
                // your code here.
            }
        });
    }

Dans Kotlin, vous pouvez créer une fonction d’extension pour cela.

Kotlin:

    fun Group.setAllOnClickListener(listener: View.OnClickListener?) {
        referencedIds.forEach { id ->
            rootView.findViewById<View>(id).setOnClickListener(listener)
        }
    }

Ensuite, appelez la fonction du groupe:

    group.setAllOnClickListener(View.OnClickListener {
        // code to perform on click event
    })
29
Cheticamp

Pour compléter la réponse acceptée pour Kotlin, les utilisateurs créent une fonction d’extension et acceptent un lambda qui ressemble davantage à l’API group.addOnClickListener { }.

Créez la fonction d'extension:

fun Group.addOnClickListener(listener: (view: View) -> Unit) {
    referencedIds.forEach { id ->
        rootView.findViewById<View>(id).setOnClickListener(listener)
    }
}

usage:

group.addOnClickListener { v ->
    Log.d("GroupExt", v)
}
4
Rodrigo Queiroz

Le meilleur moyen d'écouter les événements de clic provenant de plusieurs vues consiste à ajouter une vue transparente en tant que conteneur au-dessus de toutes les vues requises. Cette vue doit être à la fin (c'est-à-dire en haut) de toutes les vues sur lesquelles vous devez cliquer.

Exemple de vue du conteneur: 

<View
   Android:id="@+id/view_container"
   Android:layout_width="0dp"
   Android:layout_height="0dp"
   app:layout_constraintBottom_toBottomOf="@+id/view_bottom"
   app:layout_constraintEnd_toEndOf="@+id/end_view_guideline"
   app:layout_constraintStart_toStartOf="@+id/start_view_guideline"
   app:layout_constraintTop_toTopOf="parent"/>

L'exemple ci-dessus contient les quatre limites de contrainte, vous pouvez ajouter des vues à écouter ensemble. Comme c'est une vue, vous pouvez faire ce que vous voulez, comme un effet d'entraînement.

2
Vitthalk

Bien que j'apprécie l'approche générale de la { réponse de Vitthalk }, _ _ je pense qu'elle présente un inconvénient majeur et deux inconvénients mineurs.

  1. Il ne prend pas en compte les changements de position dynamiques des vues uniques

  2. Il peut enregistrer des clics pour des vues qui ne font pas partie du groupe.

  3. Ce n'est pas une solution générique à ce problème plutôt commun

Bien que je ne sois pas sûr de trouver une solution au deuxième point, il existe clairement des points faciles pour le premier et le troisième.


1. Modifications de la position comptable de l'élément dans le groupe 

C'est en fait assez simple. Vous pouvez utiliser le jeu d’outils de la disposition des contraintes pour ajuster les bords de la vue transparente . Nous utilisons simplement Barrières pour recevoir les positions les plus à gauche, les plus à droite, etc. de toute vue du groupe . Ensuite, nous pouvons ajuster la vue transparente aux barrières au lieu des vues concrètes.

3. Solution générique

En utilisant Kotlin, nous pouvons étendre la classe de groupe pour inclure une méthode qui ajoute un ClickListener à une vue, comme décrit ci-dessus . aligné sur les barrières et enregistre le ClickListener dans le dernier.

De cette façon, nous devons simplement appeler la méthode sur le groupe et ne pas avoir besoin d'ajouter les vues à la présentation manuellement à chaque fois que nous avons besoin de ce comportement.

1
Endzeit

La méthode d’extension est excellente mais vous pouvez la rendre encore meilleure en la changeant en 

fun Group.setAllOnClickListener(listener: (View) -> Unit) {
    referencedIds.forEach { id ->
        rootView.findViewById<View>(id).setOnClickListener(listener)
    }
}

Donc, l'appel serait comme ça

group.setAllOnClickListener {
    // code to perform on click event
}

Désormais, il n'est plus nécessaire de définir explicitement View.OnClickListener.

Vous pouvez également définir votre propre interface pour GroupOnClickLitener comme ceci

interface GroupOnClickListener {
    fun onClick(group: Group)
}

puis définir une méthode d'extension comme celle-ci

fun Group.setAllOnClickListener(listener: GroupOnClickListener) {
    referencedIds.forEach { id ->
        rootView.findViewById<View>(id).setOnClickListener { listener.onClick(this)}
    }
}

et l'utiliser comme ça

groupOne.setAllOnClickListener(this)
groupTwo.setAllOnClickListener(this)
groupThree.setAllOnClickListener(this)

override fun onClick(group: Group) {
    when(group.id){
        R.id.group1 -> //code for group1
        R.id.group2 -> //code for group2
        R.id.group3 -> //code for group3
        else -> throw IllegalArgumentException("wrong group id")
    }
}

La deuxième approche offre de meilleures performances si le nombre de vues est grand, car vous n'utilisez qu'un seul objet comme écouteur pour toutes les vues!

0
Reza