Android: comment gérer le clic sur un bouton

95

Ayant une solide expérience dans le domaine non-Java et non-Android, j'apprends Android.

J'ai beaucoup de confusion avec différents domaines, l'un d'eux est de savoir comment gérer les clics sur les boutons. Il y a au moins 4 façons de faire ça (!!!), elles sont brièvement listées ici

à des fins de cohérence, je les énumérerai:

  1. Avoir un membre de la View.OnClickListenerclasse dans l'activité et l'affecter à une instance qui gérera la onClicklogique dans la onCreateméthode d'activité.

  2. Créez 'onClickListener' dans la méthode d'activité 'onCreate' et affectez-le au bouton à l'aide de setOnClickListener

  3. Implémentez 'onClickListener' dans l'activité elle-même et affectez 'this' comme écouteur pour le bouton. Dans le cas où l'activité a peu de boutons, l'identifiant du bouton doit être analysé pour exécuter le gestionnaire 'onClick' pour le bouton approprié

  4. Avoir une méthode publique sur l'activité qui implémente la logique 'onClick' et l'affecter au bouton dans la déclaration xml d'activité

Question 1:

Sont-ce toutes des méthodes, y a-t-il une autre option? (Je n'ai besoin d'aucun autre, juste curieux)

Pour moi, le moyen le plus intuitif serait le dernier: il nécessite le moins de code à taper et est le plus lisible (du moins pour moi).

Cependant, je ne vois pas cette approche largement utilisée. Quels sont les inconvénients de son utilisation?

Question 2:

Quels sont les avantages / inconvénients de chacune de ces méthodes? Veuillez partager votre expérience ou un bon lien.

Tout commentaire est le bienvenu!

PS J'ai essayé de Google et de trouver quelque chose pour ce sujet, mais les seules choses que j'ai trouvées sont la description "comment" faire cela, pas pourquoi est-ce bon ou mauvais.

Budda
la source

Réponses:

147

Question 1: Malheureusement, celui dans lequel vous dites être le plus intuitif est le moins utilisé sous Android. Si je comprends bien, vous devez séparer votre interface utilisateur (XML) et vos fonctionnalités de calcul (fichiers de classe Java). Cela facilite également le débogage. Il est en fait beaucoup plus facile de lire de cette façon et de penser à Android imo.

Question 2: Je crois que les deux principalement utilisés sont les n ° 2 et 3. J'utiliserai un bouton clickButton comme exemple.

2

se présente sous la forme d'une classe anonyme.

Button clickButton = (Button) findViewById(R.id.clickButton);
clickButton.setOnClickListener( new OnClickListener() {
            
            @Override
            public void onClick(View v) {
                // TODO Auto-generated method stub
                ***Do what you want with the click here***
            }
        });

C'est mon préféré car il a la méthode onClick juste à côté de l'endroit où la variable de bouton a été définie avec le findViewById. Il semble très net et bien rangé que tout ce qui concerne cette vue du bouton clickButton se trouve ici.

Un inconvénient que mon collègue commente, c'est que vous imaginez que vous avez de nombreuses vues qui nécessitent une écoute en un clic. Vous pouvez voir que votre onCreate deviendra très long. Alors c'est pourquoi il aime utiliser:

3

Disons que vous avez, 5 boutons clics:

Assurez-vous que votre activité / fragment implémente OnClickListener

// in OnCreate

Button mClickButton1 = (Button)findViewById(R.id.clickButton1);
mClickButton1.setOnClickListener(this);
Button mClickButton2 = (Button)findViewById(R.id.clickButton2);
mClickButton2.setOnClickListener(this);
Button mClickButton3 = (Button)findViewById(R.id.clickButton3);
mClickButton3.setOnClickListener(this);
Button mClickButton4 = (Button)findViewById(R.id.clickButton4);
mClickButton4.setOnClickListener(this);
Button mClickButton5 = (Button)findViewById(R.id.clickButton5);
mClickButton5.setOnClickListener(this);


// somewhere else in your code

public void onClick(View v) {
    switch (v.getId()) {
        case  R.id.clickButton1: {
            // do something for button 1 click
            break;
        }

        case R.id.clickButton2: {
            // do something for button 2 click
            break;
        }

        //.... etc
    }
}

De cette façon, comme l'explique mon collègue, est plus nette à ses yeux, car tout le calcul onClick est géré au même endroit et n'encombre pas la méthode onCreate. Mais l'inconvénient que je vois est que le:

  1. se regarde,
  2. et tout autre objet qui pourrait être situé dans onCreate utilisé par la méthode onClick devra être transformé en champ.

Faites-moi savoir si vous souhaitez plus d'informations. Je n'ai pas répondu complètement à votre question car c'est une question assez longue. Et si je trouve des sites, je vais élargir ma réponse, en ce moment je donne juste un peu d'expérience.

Dumamilk
la source
1
Pour l'option 2, vous voudrez la faire: clickButton.setOnClickListener (new View.OnClickListener () {@Override public void onClick (View v) {// TODO what you want to do}}); pour l'aider à résoudre OnClickListener
ColossalChris
L'option 3 est probablement la plus propre et la plus simple à étendre avec le modèle MVP.
Raffaeu
L'option 2 peut encore produire ce onCreate()qui n'est pas très long. Les affectations d'écouteur de clics et les classes anonymes peuvent être prises en compte dans une méthode d'assistance distincte appelée à partir de onCreate().
Nick Alexeev le
@Colossal: Vous n'êtes pas obligé de le faire. Ajoutez une extension à la classe d'activité comme "implémente View.OnClickListener".
TomeeNS
10

# 1 J'utilise fréquemment le dernier lorsque j'ai des boutons sur la mise en page qui ne sont pas générés (mais statiques évidemment).

Si vous l'utilisez dans la pratique et dans une application métier, faites particulièrement attention ici, car lorsque vous utilisez un obfuscater source comme ProGuard, vous devrez marquer ces méthodes dans votre activité pour ne pas être obscurcies.

Pour archiver une sorte de sécurité au moment de la compilation avec cette approche, jetez un œil à Android Lint ( exemple ).


# 2 Les avantages et les inconvénients de toutes les méthodes sont presque les mêmes et la leçon devrait être:

Utilisez ce qui vous convient le mieux ou qui vous semble le plus intuitif.

Si vous devez affecter la même chose OnClickListenerà plusieurs instances de bouton, enregistrez-la dans la portée de classe (# 1). Si vous avez besoin d'un simple écouteur pour un Button, effectuez une implémentation anonyme:

button.setOnClickListener(new View.OnClickListener() {
    @Override
    public void onClick(View view) {
        // Take action.
    }
});

J'ai tendance à ne pas implémenter le OnClickListenerdans l'activité, cela devient un peu déroutant de temps en temps (surtout lorsque vous implémentez plusieurs autres gestionnaires d'événements et que personne ne sait ce que thistout cela fait).

Lukas Knuth
la source
Je suis la même chose mais je n'obtiens toujours aucune sortie pour la fonction, mon code et ma requête sont ici: stackoverflow.com/questions/25107427/…
Rocket
8

Je préfère l'option 4, mais cela a un sens intuitif pour moi car je travaille beaucoup trop dans Grails, Groovy et JavaFX. Les connexions «magiques» entre la vue et le contrôleur sont communes à tous. Il est important de bien nommer la méthode:

Dans la vue, ajoutez la méthode onClick au bouton ou à un autre widget:

    android:clickable="true"
    android:onClick="onButtonClickCancel"

Puis dans la classe, gérez la méthode:

public void onButtonClickCancel(View view) {
    Toast.makeText(this, "Cancel pressed", Toast.LENGTH_LONG).show();
}

Encore une fois, nommez clairement la méthode, quelque chose que vous devriez faire de toute façon, et la maintenance devient une seconde nature.

Un gros avantage est que vous pouvez maintenant écrire des tests unitaires pour la méthode. L'option 1 peut le faire, mais 2 et 3 sont plus difficiles.

Steve Gelman
la source
1
Je vais gaufre un peu et suggérer une cinquième option (non, pas avec Bruce Willis :)), une variante des options 2: utiliser une classe Presenter dans un framework Model-View-Presenter pour gérer les clics. Cela rend les tests automatisés BEAUCOUP plus faciles. Consultez ce lien pour plus d'informations: codelabs.developers.google.com/codelabs/android-testing/…
Steve Gelman
4

Le moyen le plus utilisé est la déclaration anonyme

    Button send = (Button) findViewById(R.id.buttonSend);
    send.setOnClickListener(new View.OnClickListener() {
        @Override
        public void onClick(View v) {
            // handle click
        }
    });

Vous pouvez également créer un objet View.OnClickListener et le définir sur bouton ultérieurement, mais vous devez toujours remplacer la méthode onClick par exemple

View.OnClickListener listener = new View.OnClickListener(){
     @Override
        public void onClick(View v) {
            // handle click
        }
}   
Button send = (Button) findViewById(R.id.buttonSend);
send.setOnClickListener(listener);

Lorsque votre activité implémente l'interface OnClickListener, vous devez remplacer la méthode onClick (View v) au niveau de l'activité. Ensuite, vous pouvez assumer cette activité en tant qu'auditeur de bouton, car elle implémente déjà l'interface et remplace la méthode onClick ()

public class MyActivity extends Activity implements View.OnClickListener{


    @Override
    public void onClick(View v) {
        // handle click
    }


    @Override
    public void onCreate(Bundle b) {
        Button send = (Button) findViewById(R.id.buttonSend);
        send.setOnClickListener(this);
    }

}

(à mon humble avis) 4ème approche utilisée lorsque plusieurs boutons ont le même gestionnaire, et vous pouvez déclarer une méthode dans la classe d'activité et affecter cette méthode à plusieurs boutons dans une mise en page xml, vous pouvez également créer une méthode pour un bouton, mais dans ce cas, je préfère déclarer les gestionnaires dans la classe d'activité.

Georgy Gobozov
la source
1

Les options 1 et 2 impliquent l'utilisation d'une classe interne qui rendra le code un peu encombrant. L'option 2 est en quelque sorte désordonnée car il y aura un auditeur pour chaque bouton. Si vous avez un petit nombre de boutons, ce n'est pas grave. Pour l'option 4, je pense que ce sera plus difficile à déboguer car vous devrez revenir en arrière et quatrième le code xml et java. J'utilise personnellement l'option 3 lorsque je dois gérer plusieurs clics de bouton.

CChi
la source
1

Mon échantillon, testé dans le studio Android 2.1

Définir le bouton dans la mise en page XML

<Button
    android:id="@+id/btn1"
    android:layout_width="wrap_content"
    android:layout_height="wrap_content" />

Détection de pulsation Java

Button clickButton = (Button) findViewById(R.id.btn1);
if (clickButton != null) {
    clickButton.setOnClickListener( new View.OnClickListener() {

        @Override
        public void onClick(View v) {
            /***Do what you want with the click here***/
        }
    });
}
Webserveis
la source
1

Pour rendre les choses plus faciles comme indiqué à la question 2, vous pouvez utiliser la méthode lambda comme celle-ci pour enregistrer la mémoire des variables et éviter de naviguer de haut en bas dans votre classe de vue

//method 1
findViewById(R.id.buttonSend).setOnClickListener(v -> {
          // handle click
});

mais si vous souhaitez appliquer l'événement de clic à votre bouton à la fois dans une méthode.

vous pouvez utiliser la question 3 de @D. Réponse Tran. Mais n'oubliez pas d'implémenter votre classe de vue avec View.OnClickListener.

En autre, utiliser correctement la question n ° 3

Michael
la source
1
Cela devrait être considéré comme la réponse moderne combinée avec des références de méthode IMO. La plupart des autres réponses n'appellent pas le fait qu'il s'agit d'un ancien code pré Java8 sur Android.
Ryan The Leach le
0

Question n ° 1 - C'est le seul moyen de gérer les clics sur les vues.

Question # 2 -
Option # 1 / Option # 4 - Il n'y a pas beaucoup de différence entre l'option # 1 et l'option # 4. La seule différence que je vois est que dans un cas, l'activité consiste à implémenter OnClickListener, tandis que dans l'autre cas, il y aurait une implémentation anonyme.

Option # 2 - Dans cette méthode, une classe anonyme sera générée. Cette méthode est un peu lourde, car vous devez la faire plusieurs fois si vous avez plusieurs boutons. Pour les classes anonymes, vous devez faire attention à la gestion des fuites de mémoire.

Option n ° 3 - Cependant, c'est un moyen facile. Habituellement, les programmeurs essaient de n'utiliser aucune méthode avant de l'écrire, et par conséquent, cette méthode n'est pas largement utilisée. Vous verrez que la plupart des gens utilisent l'option 4. Parce que c'est plus propre en terme de code.

Gaurav Arora
la source
Salut Gaurav, merci pour la réponse. Mais pouvez-vous s'il vous plaît clarifier ce que vous entendez ici: Pour les classes Anonymes, vous devez faire attention à la gestion des fuites de mémoire. Comment les fuites de mémoire viennent-elles ici?
Budda
Vous devez juste être conscient que: si vous créez une classe anonyme dans une méthode qui pourrait être appelée plusieurs fois au cours de la vie de votre application, plusieurs instances d'une classe ne seront pas créées mais plusieurs classes, y compris des instances de celles-ci. Vous pouvez éviter cela en utilisant des classes internes régulières et en instanciant les écouteurs en tant que champs d'instance. Essayez de réduire les différentes classes d'écouteurs en rendant l'état d'écoute conscient via des arguments de constructeur. Une classe interne régulière vous offre les avantages de constructeurs personnalisés et d'autres méthodes.
Risadinha
0

Il existe également des options disponibles sous la forme de diverses bibliothèques qui peuvent rendre ce processus très familier aux personnes qui ont utilisé d'autres frameworks MVVM.

https://developer.android.com/topic/libraries/data-binding/

Montre un exemple de bibliothèque officielle, qui vous permet de lier des boutons comme celui-ci:

<Button
    android:text="Start second activity"
    android:layout_width="match_parent"
    android:layout_height="wrap_content"
    android:onClick="@{() -> presenter.showList()}"
/>
Ryan The Leach
la source
0

Étape 1: Créez un fichier XML:

<?xml version="1.0" encoding="utf-8"?>
<LinearLayout
    xmlns:android="http://schemas.android.com/apk/res/android"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    android:orientation="vertical">

    <Button
        android:id="@+id/btnClickEvent"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:text="Click Me" />
</LinearLayout>

Étape 2: Créez MainActivity:

package com.scancode.acutesoft.telephonymanagerapp;

import android.app.Activity;
import android.os.Bundle;
import android.view.View;
import android.widget.Button;

public class MainActivity extends Activity implements View.OnClickListener {

    Button btnClickEvent;
    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);

        btnClickEvent = (Button) findViewById(R.id.btnClickEvent);
        btnClickEvent.setOnClickListener(MainActivity.this);

    }

    @Override
    public void onClick(View v) {
        //Your Logic
    }
}

HappyCoding!

Manikanta Reddy
la source