web-dev-qa-db-fra.com

En quoi l'attribut XML d'Android: onClick est-il différent de setOnClickListener?

À partir de là, j'ai lu que vous pouvez affecter un gestionnaire onClick à un bouton de deux manières.

Utilisation de l'attribut XML Android:onClick dans lequel vous utilisez simplement le nom d'une méthode publique avec signaturevoid name(View v) ou à l'aide de la méthode setOnClickListener dans laquelle vous passez un objet implémentant l'interface OnClickListener . Ce dernier nécessite souvent une classe anonyme que je n'aime pas personnellement (goût personnel) ou la définition d'une classe interne qui implémente la OnClickListener.

En utilisant l'attribut XML, il vous suffit de définir une méthode au lieu d'une classe, alors je me demandais si la même chose pouvait être faite via du code et non dans la présentation XML.

392
emitrax

Non, ce n'est pas possible via code. Android implémente simplement la OnClickListener pour vous lorsque vous définissez l'attribut Android:onClick="someMethod".

Ces deux extraits de code sont égaux, ils sont simplement implémentés de deux manières différentes.

Implémentation du code

Button btn = (Button) findViewById(R.id.mybutton);

btn.setOnClickListener(new View.OnClickListener() {
    @Override
    public void onClick(View v) {
        myFancyMethod(v);
    }
});

// some more code

public void myFancyMethod(View v) {
    // does something very interesting
}

Ci-dessus, une implémentation de code d'un OnClickListener. Et ceci est l'implémentation XML.

implémentation XML

<?xml version="1.0" encoding="utf-8"?>
<!-- layout elements -->
<Button Android:id="@+id/mybutton"
    Android:layout_width="wrap_content"
    Android:layout_height="wrap_content"
    Android:text="Click me!"
    Android:onClick="myFancyMethod" />
<!-- even more layout elements -->

En arrière-plan, Android ne fait rien d'autre que le code Java, appelant votre méthode lors d'un événement click.

Notez qu'avec le XML ci-dessus, Android recherchera la méthode onClickmyFancyMethod() uniquement dans l'activité en cours. Il est important de se rappeler que vous utilisez des fragments, car même si vous ajoutez le code XML ci-dessus à l'aide d'un fragment, Android ne cherchera pas la méthode onClick dans le fichier .Java de le fragment utilisé pour ajouter le XML.

Une autre chose importante que j'ai remarquée. Vous avez mentionné que vous ne préfériez pas les méthodes anonymes . Vous vouliez dire que vous n'aimez pas les anonymes classes.

576
Octavian Damiean

Quand j'ai vu la réponse en haut, cela m'a fait comprendre que mon problème n'était pas de placer le paramètre (View v) sur la méthode de fantaisie:

public void myFancyMethod(View v) {}

Lorsque vous essayez d'y accéder à partir du xml, vous devez utiliser

Android:onClick="myFancyMethod"/>

J'espère que ça aide quelqu'un.

82
jp093121

Android:onClick est destiné aux API de niveau 4 et supérieur. Par conséquent, si vous ciblez <1,6, vous ne pourrez pas l'utiliser.

73
James

Vérifiez si vous avez oublié de rendre la méthode publique!

30
Ruivo

La spécification de l'attribut _Android:onClick_ a pour résultat que Button instance appelle setOnClickListener en interne. Il n'y a donc absolument aucune différence.

Pour bien comprendre, voyons comment l'attribut XML onClick est géré par le framework.

Lorsqu'un fichier de présentation est gonflé, toutes les vues spécifiées sont instanciées. Dans ce cas spécifique, l'instance Button est créée à l'aide du constructeur public Button (Context context, AttributeSet attrs, int defStyle) . Tous les attributs de la balise XML sont lus à partir du groupe de ressources et transmis sous la forme AttributeSet au constructeur.

La classe Button est héritée de la classe View qui entraîne l'appel du constructeur View qui prend en charge le paramétrage du gestionnaire de rappel au clic via setOnClickListener.

L'attribut onClick défini dans attrs.xml est désigné dans View.Java par _R.styleable.View_onClick_.

Voici le code de View.Java qui effectue la majeure partie du travail pour vous en appelant setOnClickListener par lui-même.

_ case R.styleable.View_onClick:
            if (context.isRestricted()) {
                throw new IllegalStateException("The Android:onClick attribute cannot "
                        + "be used within a restricted context");
            }

            final String handlerName = a.getString(attr);
            if (handlerName != null) {
                setOnClickListener(new OnClickListener() {
                    private Method mHandler;

                    public void onClick(View v) {
                        if (mHandler == null) {
                            try {
                                mHandler = getContext().getClass().getMethod(handlerName,
                                        View.class);
                            } catch (NoSuchMethodException e) {
                                int id = getId();
                                String idText = id == NO_ID ? "" : " with id '"
                                        + getContext().getResources().getResourceEntryName(
                                            id) + "'";
                                throw new IllegalStateException("Could not find a method " +
                                        handlerName + "(View) in the activity "
                                        + getContext().getClass() + " for onClick handler"
                                        + " on view " + View.this.getClass() + idText, e);
                            }
                        }

                        try {
                            mHandler.invoke(getContext(), View.this);
                        } catch (IllegalAccessException e) {
                            throw new IllegalStateException("Could not execute non "
                                    + "public method of the activity", e);
                        } catch (InvocationTargetException e) {
                            throw new IllegalStateException("Could not execute "
                                    + "method of the activity", e);
                        }
                    }
                });
            }
            break;
_

Comme vous pouvez le constater, setOnClickListener est appelé pour enregistrer le rappel, comme nous le faisons dans notre code. La seule différence est qu'il utilise _Java Reflection_ pour appeler la méthode de rappel définie dans notre activité.

Voici la raison des problèmes mentionnés dans d'autres réponses:

  • La méthode de rappel doit être publique : Puisque Java Class getMethod est utilisé, seules les fonctions avec spécificateur d'accès public sont recherchées. Sinon, soyez prêt à gérer l'exception IllegalAccessException.
  • Lors de l'utilisation de Button avec onClick dans Fragment, le rappel doit être défini dans Activity : getContext().getClass().getMethod() restreint la recherche de méthode au contexte actuel, à savoir Activity en casse. de fragment. Par conséquent, la méthode est recherchée dans la classe d'activité et non dans la classe de fragments.
  • La méthode de rappel doit accepter le paramètre View : Depuis Java Class getMethod recherche une méthode qui accepte _View.class_ en tant que paramètre .
26
Manish Mulimani

Notez que si vous souhaitez utiliser la fonctionnalité XML onClick, la méthode correspondante doit avoir un paramètre dont le type doit correspondre à l'objet XML.

Par exemple, un bouton sera lié à votre méthode par son nom: Android:onClick="MyFancyMethod" mais la déclaration de la méthode doit afficher: ...MyFancyMethod(View v) {....

Si vous essayez d'ajouter cette fonctionnalité à un élément de men, la syntaxe sera exactement la même dans le fichier XML, mais votre méthode sera déclarée comme: ...MyFancyMethod(MenuItem mi) {...

14
Antoine Lizée

Il y a de très bonnes réponses ici, mais je veux ajouter une ligne:

Dans Android:onclick en XML, Android utilise réflexion Java derrière la scène pour gérer cela.

Et comme expliqué ici la réflexion ralentit toujours la performance. (surtout sur Dhalvik VM). Enregistrer onClickListener est un meilleur moyen.

11
Krupal Shah

Un autre moyen de définir vos écouteurs au clic consiste à utiliser XML. Ajoutez simplement l'attribut Android: onClick à votre tag.

Il est recommandé d'utiliser l'attribut xml "onClick" sur une classe anonyme Java lorsque cela est possible.

Tout d’abord, regardons la différence de code:

attribut XML/attribut onClick

Partie XML

<Button
    Android:layout_width="wrap_content"
    Android:layout_height="wrap_content"
    Android:id="@+id/button1" 
    Android:onClick="showToast"/>

Partie Java

public void showToast(View v) {
    //Add some logic
}

Anonymous Java Class/setOnClickListener

Partie XML

<Button
    Android:layout_width="wrap_content"
    Android:layout_height="wrap_content"/>

Partie Java

findViewById(R.id.button1).setOnClickListener(
    new View.OnClickListener() {
        @Override
        public void onClick(View v) {
            //Add some logic
        }
});

Voici les avantages de l'utilisation de l'attribut XML sur une classe Java anonyme:

  • Avec la classe Anonymous Java, nous devons toujours spécifier un identifiant pour nos éléments, mais avec l'attribut XML, l'identifiant peut être omis.
  • Avec la classe Anonymous Java, nous devons rechercher activement l'élément dans la vue (partie findViewById), mais avec l'attribut XML Android le fait pour nous.
  • Anonyme Java La classe nécessite au moins 5 lignes de code, comme on peut le constater, mais avec l'attribut XML, 3 lignes de code suffisent.
  • Avec la classe Anonymous Java, nous devons nommer notre méthode "onClick", mais avec l'attribut XML, nous pouvons ajouter le nom de votre choix, ce qui facilitera considérablement la lisibilité de notre code.
  • L'attribut XML “onClick” a été ajouté par Google lors de la publication de l'API de niveau 4, ce qui signifie que la syntaxe est un peu plus moderne et que la syntaxe moderne est presque toujours meilleure.

Bien sûr, il n’est pas toujours possible d’utiliser l’attribut XML, voici les raisons pour lesquelles nous ne l’aurions pas choisi:

  • Si nous travaillons avec des fragments. L'attribut onClick ne peut être ajouté qu'à une activité. Par conséquent, si nous avons un fragment, nous devrions utiliser une classe anonyme.
  • Si nous souhaitons déplacer l'auditeur onClick vers une classe séparée (peut-être si c'est très compliqué et/ou si nous souhaitons le réutiliser dans différentes parties de notre application), nous ne voudrions pas utiliser l'attribut xml Soit.
5
Bestin John

En utilisant l'attribut XML, il vous suffit de définir une méthode au lieu d'une classe, alors je me demandais si la même chose pouvait être faite via du code et non dans la présentation XML.

Oui, vous pouvez rendre votre fragment ou activity implémenter View.OnClickListener

et lorsque vous initialisez vos nouveaux objets de vue dans le code, vous pouvez simplement faire mView.setOnClickListener(this);

et ceci configure automatiquement tous les objets de vue du code pour utiliser la méthode onClick(View v) que votre fragment ou activity etc.

pour distinguer la vue qui a appelé la méthode onClick, vous pouvez utiliser une instruction switch sur la méthode v.getId().

Cette réponse est différente de celle qui dit "Non, ce n'est pas possible via un code"

4
CQM

Avec Java 8, vous pourriez probablement utiliser Référence de la méthode pour obtenir ce que vous voulez.

Supposons qu'il s'agit de votre gestionnaire d'événement onClick pour un bouton.

private void onMyButtonClicked(View v) {
    if (v.getId() == R.id.myButton) {
        // Do something when myButton was clicked
    }
}

Ensuite, vous passez onMyButtonClicked référence de méthode d’instance dans un appel setOnClickListener() comme ceci.

Button myButton = (Button) findViewById(R.id.myButton);
myButton.setOnClickListener(this::onMyButtonClicked);

Cela vous permettra d'éviter explicitement de définir vous-même une classe anonyme. Je dois toutefois souligner que la référence de méthode de Java 8 n'est en réalité qu'un sucre syntaxique. En fait, cela crée une instance de la classe anonyme (comme le faisait l'expression lambda), d'où la même prudence que lorsque le gestionnaire d'événements de style expression-lambda était appliqué lorsque vous annulez l'enregistrement de votre gestionnaire d'événements. Ceci article explique vraiment Nice.

PS Pour ceux qui sont curieux de savoir comment puis-je vraiment utiliser la fonctionnalité de langage Java 8 dans Android, cette bibliothèque est une gracieuseté de la bibliothèque retrolambda .

4
onelaview
   Add Button in xml and give onclick attribute name that is the name of Method.
   <!--xml --!>
   <Button
  Android:id="@+id/btn_register"
  Android:layout_margin="1dp"
  Android:onClick="addNumber"
  Android:text="Add"
  />


    Button btnAdd = (Button) findViewById(R.id.mybutton); btnAdd.setOnClickListener(new View.OnClickListener() {
   @Override
    public void onClick(View v) {
      addNumber(v);
    }
    });

  Private void addNumber(View v){
  //Logic implement 
    switch (v.getId()) {
    case R.id.btnAdd :
        break;
     default:
        break;
    }}
4
jeet parmar

Prenant en charge la réponse de Ruivo, oui, vous devez déclarer la méthode "publique" pour pouvoir utiliser XML onclick d'Android. Je développe une application ciblant les API de niveau 8 (minSdk ...) à 16 (targetSdk ...).

Je déclarais ma méthode privée et cela provoquait une erreur, la déclarant simplement publique fonctionne très bien.

3
Waqas Hasan

How Android:onclick xml attribute works in Android

utilisez simplement le nom de la méthode dans Android: valeur de l'attribut onClick. Assurez-vous que le mot clé public est utilisé avant le nom de la méthode.

exemple XML sur Android onClick

3
c49

Soyez prudent, bien que Android:onClick XML semble être un moyen pratique de gérer les clics, l’implémentation setOnClickListener fait quelque chose de plus que l’ajout de onClickListener. En effet, la propriété view clickable a la valeur true.

Bien que cela ne pose peut-être pas un problème sur la plupart des Android implémentations, selon le constructeur du téléphone, button est toujours défini par défaut sur clickable = true, mais les autres constructeurs de certains modèles de téléphone peuvent avoir une valeur par défaut clickable = false sur les vues non de boutons. .

Donc, définir le code XML ne suffit pas, vous devez penser tout le temps à ajouter Android:clickable="true" sur un bouton, et si vous avez un périphérique où la valeur par défaut est clickable = true et que vous oubliez même une fois de mettre cet attribut XML, vous ne remarquerez pas le problème au moment de l'exécution mais obtiendrez les commentaires sur le marché quand ce sera entre les mains de vos clients!

De plus, nous ne pouvons jamais être sûrs de la façon dont proguard obfusquera et renommera les attributs XML et la méthode de classe, donc pas sûr à 100% qu’ils n’auront jamais un bogue un jour.

Donc, si vous ne voulez jamais avoir de problèmes et n'y pensez jamais, il est préférable d'utiliser setOnClickListener ou des bibliothèques comme ButterKnife avec l'annotation @OnClick(R.id.button)

1
Livio

Supposons que vous souhaitiez ajouter un événement click comme ceci main.xml

<Button
    Android:id="@+id/btn_register"
    Android:layout_margin="1dp"
    Android:layout_marginLeft="3dp"
    Android:layout_marginTop="10dp"
    Android:layout_weight="2"
    Android:onClick="register"
    Android:text="Register"
    Android:textColor="#000000"/>

Dans le fichier Java, vous devez écrire une méthode comme celle-ci.

public void register(View view) {
}
1
nazrul islam

La meilleure façon de faire est d'utiliser le code suivant:

 Button button = (Button)findViewById(R.id.btn_register);
 button.setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View v) {
                //do your fancy method
            }
        });
0
Katarina Dabo

Je suis Ecrire ce code dans un fichier xml ...

<Button
    Android:id="@+id/btn_register"
    Android:layout_margin="1dp"
    Android:layout_marginLeft="3dp"
    Android:layout_marginTop="10dp"
    Android:layout_weight="2"
    Android:onClick="register"
    Android:text="Register"
    Android:textColor="#000000"/>

Et écrivez ce code dans un fragment ...

public void register(View view) {
}
0
user2786249