Meilleur moyen d’implémenter View.OnClickListener dans Android

Supposons que nous ayons une activité avec beaucoup de vues sur laquelle OnClickListener doit être enregistré.

La manière la plus courante d’implémenter ceci est de laisser la sous-classe d’activité implémenter le OnClickListener, quelque chose comme ceci:

 public class ActivityMain extends Activity implements View.OnClickListener { @Override public void onClick(View view) { switch (view.getId()) { //handle multiple view click events } } } 

J’aime l’implémenter pour créer une classe privée dans Activity-Subclass et laisser cette classe interne implémenter OnClickListener:

 public class ActivityMain extends Activity implements View.OnClickListener { private class ClickListener implements View.OnClickListener { @Override public void onClick(View view) { switch (view.getId()) { //handle multiple view click events } } } } 

De cette façon, le code semble plus organisé et facile à maintenir.

De plus, en parlant des relations “Is-a”, “Has-a”, cette dernière semble être une bonne pratique car maintenant la sous-classe d’activité aurait une relation “Has-a” avec ClickListener. Alors que dans l’ancienne méthode, nous dirions que notre sous-classe d’activité “Is-a” ClickListener, qui n’est pas complètement vraie.

Notez que je ne suis pas concerné par la surcharge de mémoire que ce dernier causerait.

De plus, l’ajout du tag onClick dans xml est complètement hors de question.

Alors, quelle est la meilleure façon de mettre en œuvre un ClickListener?

S’il vous plaît ne pas suggérer des bibliothèques comme RoboGuice ou ButterKnife etc.

METTRE À JOUR:

J’aimerais partager l’approche que j’ai finalement adoptée.

Je mets directement en œuvre l’auditeur dans Activity / Fragment.

En ce qui concerne la conception de POO. L’approche “HAS-A” n’offre aucun avantage pratique et prend même plus de mémoire. Compte tenu de la quantité de classes nestedes (et de la surcharge de mémoire) que nous allons créer pour chaque écouteur similaire que nous implémentons, cette approche doit être évitée.

Tout d’abord, il n’y a pas de meilleure pratique définie par Android concernant l’enregistrement des auditeurs de clics. Cela dépend totalement de votre cas d’utilisation.

L’implémentation de l’interface View.OnClickListener dans Activity est la voie à suivre. Comme Android recommande fortement l’implémentation de l’interface, qu’il s’agisse d’une activité ou d’un fragment.

Maintenant que vous avez décrit:

 public class ActivityMain extends Activity implements View.OnClickListener { private class ClickListener implements View.OnClickListener { @Override public void onClick(View view) { switch (view.getId()) { //handle multiple view click events } } } } 

Ceci est votre approche. Maintenant, c’est votre façon d’implémenter et il n’y a rien de mal à cela si vous n’êtes pas concerné par la surcharge de mémoire. Mais quel est l’avantage de créer la classe interne et d’implémenter View.OnClickListener si vous pouvez simplement l’implémenter dans la classe principale, ce qui peut également vous View.OnClickListener la clarté et la simplicité du code dont vous avez besoin.

Donc, c’est juste une discussion plutôt que d’obtenir la meilleure solution possible pour l’implémentation de View.OnClickListener, car si vous allez avec le point pratique de tout le monde, vous opterez pour une solution simple et efficace en termes de mémoire.

Je préférerais donc la manière conventionnelle. Il garde les choses simples et efficaces. Vérifiez le code ci-dessous:

 @Override public void onClick(View view) { switch (view.getId()) { //handle multiple view click events } } 

PS: Votre approche va certainement augmenter les lignes de code: P;)

Tout d’abord, laissez les bases claires ici ..

En implémentant une interface, votre classe ne devient pas ça … comme vous l’avez dit:

“Notre sous-classe d’activité” est un “ClickListener, qui n’est pas tout à fait vrai”.

Votre classe ne peut avoir une relation “Is-a” que si elle prolonge, dans ce cas, une Activity . L’implémentation d’une interface signifie qu’elle peut se comporter comme quelle interface a défini son contrat.

Un exemple:

classe Peter étend Human .. signifie que Peter est un humain ..

classe Peter peut également mettre en œuvre programmeur, musicien, mari, etc. signifie que Peter peut se comporter comme ci-dessus.

En ce qui concerne les meilleures pratiques, vous pouvez créer une classe entièrement distincte qui implémente OnClickListener comme ceci:

 class MyListener implements View.OnClickListener{ @Override public void onClick(View view) { // do whatever you want here based on the view being passed } } 

Et dans votre Activity principale, vous pouvez instancier MyListener et appeler onClick() et y passer votre vue:

 MyListener listener = new MyListener(); Button b = null; @Override public void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.activity_main); button = (Button)findViewById(R.id.button); listener.onClick(button); } 

J’utilise button.setOnClickListener(this); où mon Activity implements View.OnClickListener , puis récupère l’ID du Button dans une méthode distincte. Voir ci-dessous pour un exemple:

 public class MyActivity extends ActionBarActivity implements View.OnClickListener { @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.YOUR_LAYOUT); ... Button myFirstButton = (Button) findViewById(R.id.YOUR_FIRST_BUTTON); myFirstButton.setOnClickListener(this); Button mySecondButton = (Button) findViewById(R.id.YOUR_SECOND_BUTTON); mySecondButton.setOnClickListener(this); ... } ... @Override public void onClick(View v) { Button b = (Button) v; switch(b.getId()) { case R.id.YOUR_FIRST_BUTTON: // Do something break; case R.id.YOUR_SECOND_BUTTON: // Do something break; ... } } ... } 

Ici, vous pouvez créer un object btnClickListner et ensuite appeler cet object btnCLickLisner quand vous voulez effectuer les actions onCLieck pour les boutons.

Supposons, dans mon activité, que j’ai un bouton de 5 à 10 et que l’écriture de chaque bouton séparément est une mauvaise idée. Donc, pour surmonter cela, nous pouvons utiliser comme ci-dessous ..

enregistrer vos boutons

 Button button1 = (Button)findViewById(R.id.button1); Button button2 = (Button)findViewById(R.id.button2); Button button3 = (Button)findViewById(R.id.button3); Button button4 = (Button)findViewById(R.id.button4); Button button5 = (Button)findViewById(R.id.button5); 

Ici, je mets le lecteur en ligne sur mes boutons après un clic

 button1.setOnClickListener(btnClickListner); button2.setOnClickListener(btnClickListner); button3.setOnClickListener(btnClickListner); button4.setOnClickListener(btnClickListner); button5.setOnClickListener(btnClickListner); 

Voici l’implémentation de btnClick Listner

 View.OnClickListener btnClickListner = new OnClickListener() { @Override public void onClick( View v ) { // TODO Auto-generated method stub if( button1.getId() == v.getId() ) { //Do Button1 click operations here } else if( button2.getId() == v.getId() ) { // Do Button2 click operations here } else if( button3.getId() == v.getId() ) { // Do Button3 click operations here } else if( button4.getId() == v.getId() ) { // Do Button4 click operations here } else if( button5.getId() == v.getId() ) { // Do Button5 click operations here } } } 

J’ai trouvé en utilisant Butterknife pour le code propre. Et parce qu’il utilise la génération de code (pas de reflections), il a peu de performances supplémentaires.

 public class ActivityMain extends Activity { @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.activity_main); ButterKnife.inject(this); } @OnClick(R.id.button_foo) void onFoodClicked() { // Do some foo } @OnClick(R.id.button_bar) void onBarClicked() { // do some bar } } 

Votre ClickListener est une classe interne non statique. Le couplage de ce has-a n’est pas différent de si votre classe Activity implémentait View.OnClickListener . C’est parce que votre ClickListener interne nécessite une instance de ActivityMain et ne peut vraiment pas être réutilisée. Je dirais que vous êtes sur l’ingénierie et ne gagne rien en fait.

EDIT: Pour répondre à votre question, j’aime avoir View.OnClickListener anonyme pour chaque widget. Je pense que cela crée la meilleure séparation de la logique. J’ai aussi des méthodes comme setupHelloWorldTextView(TextView helloWorldTextView); où j’ai mis toute ma logique liée à ce widget.

La première approche est meilleure que l’autre car c’est pourquoi View.OnClickListener est une Interface au lieu d’une abstract class . En outre, la dernière peut présenter des fuites dans diverses situations, car vous utilisez une classe interne non statique.

Pour ce cas particulier, je dirais que maintenir une seule instance d’OnClickListener est la meilleure approche pour vous. Vous aurez une relation “Has-a” et vous n’aurez pas besoin de créer plusieurs instances puisque vous gérez le comportement à l’aide de l’ID de vue dans le onClick(View view) .

 public class ActivityMain extends Activity implements View.OnClickListener { private View.OnClickListener mClickListener = new View.OnClickListener() { @Override public void onClick(View view) { switch (view.getId()) { //handle multiple view click events } } }; } 

Tout simplement, vous utilisez like not implémente la sous-classe ou ne gérez pas un événement de clic comme cela.

 android.view.View.OnClickListener method_name = new android.view.View.OnClickListener() { @Override public void onClick(View v) { // TODO Auto-generated method stub // put your code . } }; 

et gérer l’événement clic dans le bouton ya tout type d’événement de clic comme

 button_name.setOnClickListener(method_name); 

son travail très simplement merci

Cela dépend vraiment de ce que vous voulez atteindre. Si vous avez par exemple une fonctionnalité complexe avec des threads, des dépendances, etc., je préfère personnellement le découpler complètement de l’activité dans une classe distincte XyzAction , qui fait les choses lourdes, connaît certains Invoker et leur renvoie des résultats si nécessaire. Mes Invoker sont essentiellement des objects qui implémentent OnClick / OnTouch / etc. Listener se lient aux actions nécessaires. Par exemple, il pourrait y avoir un LoginInvoker implémentant OnClickListener pour un Button et un ImageView ainsi qu’un ActionListener générique qui est appelé lorsqu’un utilisateur clique sur MenuItem . L’ Invoker a des méthodes de mise à jour pour montrer la progression à l’utilisateur et le résultat de l’action liée. L’action publie des mises à jour sur ses Invoker et peut être récupérée, si tous meurent, car elle n’a aucune connexion à l’interface utilisateur.

Pour les actions moins complexes, je les couple directement au composant Android (c.-à-d. Activity / Feagment / View ) et les appelle également Actions , la grande différence étant qu’elles implémentent directement les rappels de l’interface utilisateur.

Dans les deux cas, je déclare les actions en tant que membres, je peux donc voir rapidement les actions spécifiques sockets en charge par le composant Android.

S’il y a quelque chose de sortingvial comme “afficher un Toast si le bouton est pressé”, j’utilise des classes internes anonymes pour les rappels de l’interface utilisateur, car vous ne vous souciez généralement pas beaucoup de leur maintenabilité.

Une petite remarque à cela, et peut-être un peu de sujet.

Quoi, si nous n’implémentons pas simplement OnClickListener et nous avons un tas d’autres Listeners / Callback à implémenter. Selon moi, il serait compliqué d’implémenter tout cela dans la classe au lieu d’utiliser des classes anonymes / lambda. Il est difficile de se rappeler quelle méthode appartient à quelle interface.

Donc, si nous devons implémenter une interface (dans ce cas, OnClickListener) plusieurs fois, cela peut être une bonne solution pour implémenter sur une base de classe et utiliser le switch / case.

Mais si nous devons implémenter plusieurs interfaces, cela peut être une bonne solution pour utiliser des classes / lambda anonymes