111

Having a solid experience in non-Java and non-Android area, I'm learning Android.

I have a lot of confusion with different areas, one of them is how to handle button clicks. There are at least 4 way of doing that (!!!), they are briefly listed here

for consistency purpose I will list them:

  1. Have a member of the View.OnClickListener class in the activity and assign it to an instance that will handle onClick logic in the onCreate activity method.

  2. Create 'onClickListener' in the 'onCreate' activity method and assign it to the button using setOnClickListener

  3. Implement 'onClickListener' in activity itself and assign 'this' as a listener for the button. For the case if activity has few buttons, button id should be analyzed to execute 'onClick' handler for the proper button

  4. Have public method on the activity that implements 'onClick' logic and assign it to the button in the activity xml declaration

Question #1:

Are those all methods, is there any other option? (I don't need any other, just curious)

For me, the most intuitive way would be the latest one: it requires the least amount of code to be typed and is the most readable (at least for me).

Though, I don't see this approach used widely. What are cons for using it?

Question #2:

What are pros/cons for each of these methods? Please share either your experience or a good link.

Any feedback is welcome!

P.S. I've tried to Google and find something for this topic, but the only things I've found are description "how" to do that, not why is it good or bad.

Aditya Vyas-Lakhan
  • 13,409
  • 16
  • 61
  • 96
Budda
  • 18,015
  • 33
  • 124
  • 206

10 Answers10

165

Question 1: Unfortunately the one in which you you say is most intuitive is the least used in Android. As I understand, you should separate your UI (XML) and computational functionality (Java Class Files). It also makes for easier debugging. It is actually a lot easier to read this way and think about Android imo.

Question 2: I believe the two mainly used are #2 and #3. I will use a Button clickButton as an example.

2

is in the form of an anonymous class.

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***
            }
        });

This is my favorite as it has the onClick method right next to where the button variable was set with the findViewById. It seems very neat and tidy that everything that deals with this clickButton Button View is located here.

A con that my coworker comments, is that imagine you have many views that need onclick listener. You can see that your onCreate will get very long in length. So that why he likes to use:

3

Say you have, 5 clickButtons:

Make sure your Activity/Fragment implement 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
    }
}

This way as my coworker explains is neater in his eyes, as all the onClick computation is handled in one place and not crowding the onCreate method. But the downside I see is, that the:

  1. views themselves,
  2. and any other object that might be located in onCreate used by the onClick method will have to be made into a field.

Let me know if you would like more information. I didn't answer your question fully because it is a pretty long question. And if I find some sites I will expand my answer, right now I'm just giving some experience.

Community
  • 1
  • 1
dumamilk
  • 2,144
  • 1
  • 12
  • 11
  • 1
    For option 2 you'll want to make it: clickButton.setOnClickListener( new View.OnClickListener() {@Override public void onClick(View v) { //TODO what you want to do } }); to help it resolve OnClickListener – Chris Klingler Mar 25 '15 at 13:54
  • Option 3 is probably the cleanest and easiest to extend with MVP pattern. – Raffaeu Jan 26 '16 at 12:05
  • Option 2 can still produce `onCreate()` that's not awfully long. The click listener assignments and the anonymous classes can be factored out into a separate helper method that's called from `onCreate()`. – Nick Alexeev Feb 29 '16 at 07:28
  • @Colossal: You do not have to do it. Add extension to the Activity Class like " implements View.OnClickListener ". – TomeeNS Nov 27 '18 at 21:52
12

#1 I use the last one frequently when having buttons on the layout which are not generated (but static obviously).

If you use it in practice and in a business application, pay extra attention here, because when you use source obfuscater like ProGuard, you'll need to mark these methods in your activity as to not be obfuscated.

For archiving some kind of compile-time-security with this approach, have a look at Android Lint (example).


#2 Pros and cons for all methods are almost the same and the lesson should be:

Use what ever is most appropriate or feels most intuitive to you.

If you have to assign the same OnClickListener to multiple button instances, save it in the class-scope (#1). If you need a simple listener for a Button, make an anonymous implementation:

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

I tend to not implement the OnClickListener in the activity, this gets a little confusing from time to time (especially when you implement multiple other event-handlers and nobody knows what this is all doing).

Lukas Knuth
  • 25,449
  • 15
  • 83
  • 111
  • I am following the same but still getting no output for function , my code and query is here : http://stackoverflow.com/questions/25107427/can-we-make-2-different-functions-in-same-jni-in-android-ndk-using-c/25187447?noredirect=1#comment39221983_25187447 – Rocket Aug 07 '14 at 19:42
9

I prefer option 4, but it makes intuitive sense to me because I do far too much work in Grails, Groovy, and JavaFX. "Magic" connections between the view and the controller are common in all. It is important to name the method well:

In the view,add the onClick method to the button or other widget:

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

Then in the class, handle the method:

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

Again, name the method clearly, something you should do anyway, and the maintenance becomes second-nature.

One big advantage is that you can write unit tests now for the method. Option 1 can do this, but 2 and 3 are more difficult.

Steve Gelman
  • 874
  • 9
  • 16
  • 1
    I'm going to waffle a little and suggest a fifth option (no, not starring Bruce Willis :) ), a variant of options 2: use a Presenter class in a Model-View-Presenter framework to handle clicks. It makes automated testing MUCH easier. Check out this link for better information: https://codelabs.developers.google.com/codelabs/android-testing/index.html – Steve Gelman Jun 29 '17 at 13:50
4

Most used way is, anonymous declaration

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

Also you can create View.OnClickListener object and set it to button later, but you still need to override onClick method for example

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

When your activity implements OnClickListener interface you must override onClick(View v) method on activity level. Then you can assing this activity as listener to button, because it already implements interface and overrides the onClick() method

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);
    }

}

(imho) 4-th approach used when multiple buttons have same handler, and you can declare one method in activity class and assign this method to multiple buttons in xml layout, also you can create one method for one button, but in this case I prefer to declare handlers inside activity class.

laalto
  • 150,114
  • 66
  • 286
  • 303
Georgy Gobozov
  • 13,633
  • 8
  • 72
  • 78
2

Step 1:Create an XML File:

<?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>

Step 2:Create 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!

alakh125
  • 41
  • 9
Manikanta Reddy
  • 631
  • 9
  • 15
2

To make things easier asp Question 2 stated, you can make use of lambda method like this to save variable memory and to avoid navigating up and down in your view class

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

but if you wish to apply click event to your button at once in a method.

you can make use of Question 3 by @D. Tran answer. But do not forget to implement your view class with View.OnClickListener.

In other to use Question #3 properly

marc_s
  • 732,580
  • 175
  • 1,330
  • 1,459
Michael
  • 588
  • 11
  • 19
  • 1
    This should be considered the modern answer combined with method references IMO. Most of the other answers don't call out the fact they they are old pre Java8 code on Android. – Ryan Leach Jun 04 '18 at 01:52
1

Option 1 and 2 involves using inner class that will make the code kind of clutter. Option 2 is sort of messy because there will be one listener for every button. If you have small number of button, this is okay. For option 4 I think this will be harder to debug as you will have to go back and fourth the xml and java code. I personally use option 3 when I have to handle multiple button clicks.

CChi
  • 3,054
  • 1
  • 20
  • 15
1

My sample, Tested in Android studio 2.1

Define button in xml layout

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

Java pulsation detect

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***/
        }
    });
}
Codelaby
  • 2,604
  • 1
  • 25
  • 25
0

Question#1 - These are the only way to handle view clicks.

Question#2 -
Option#1/Option#4 - There's not much difference between option#1 and option#4. The only difference I see is in one case activity is implementing the OnClickListener, whereas, in the other case, there'd be an anonymous implementation.

Option#2 - In this method an anonymous class will be generated. This method is a bit cumborsome, as, you'd need to do it multiple times, if you have multiple buttons. For Anonymous classes, you have to be careful for handling memory leaks.

Option#3 - Though, this is a easy way. Usually, Programmers try not to use any method until they write it, and hence this method is not widely used. You'd see mostly people use Option#4. Because it is cleaner in term of code.

Gaurav Arora
  • 1,805
  • 13
  • 13
  • Hi Gaurav, thank you for answer. But can you please clarify what you mean here: For Anonymous classes, you have to be careful for handling memory leaks. How memory leaks come here? – Budda Feb 09 '13 at 01:26
  • You just have to be aware that: if you create an anonymous class inside a method that might get called multiple times during the lifetime of your app not several instances of one class will be created but several classes including instances of them. You can avoid that by using regular inner classes and instantiating the listeners as instance fields. Try to reduce the different listeners classes by making the listener state aware via constructor arguments. A regular inner class gives you the benefit of custom constructors and other methods. – Risadinha Oct 15 '13 at 09:26
0

There are also options available in the form of various libraries that can make this process very familiar to people that have used other MVVM frameworks.

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

Shows an example of an official library, that allows you to bind buttons like this:

<Button
    android:text="Start second activity"
    android:layout_width="match_parent"
    android:layout_height="wrap_content"
    android:onClick="@{() -> presenter.showList()}"
/>
Ryan Leach
  • 4,262
  • 5
  • 34
  • 71