449

From that I've read you can assign a onClick handler to a button in two ways.

Using the android:onClick XML attribute where you just use the name of a public method with the signaturevoid name(View v) or by using the setOnClickListener method where you pass an object that implement the OnClickListener interface. The latter often requires an anonymous class which personally I don't like (personal taste) or defining an internal class that implements the OnClickListener.

By using the XML attribute you just need to define a method instead of a class so I was wondering if the same can be done via code and not in the XML layout.

Octavian Helm
  • 39,405
  • 19
  • 98
  • 102
emitrax
  • 4,565
  • 3
  • 18
  • 8
  • 4
    I read your problem and I think you are stuck at same place just like me. I came across a very good video which helped me very much in resolving my issue. Find the video on following link: http://www.youtube.com/watch?v=MtmHURWKCmg&feature=youtu.be I hope this will help you as well :) – user2166292 Mar 13 '13 at 16:11
  • 9
    For those who want to save time watching the video posted in the comment above, it simply demonstrates how two buttons can have the same method for it's `onClick` attribute in the layout file. This is done thanks to the parameter `View v`. You simply check `if (v == findViewById(R.id.button1`)) etc.. – CodyBugstein Sep 23 '14 at 14:09
  • 14
    @Imray I would think it's better to use `v.getId() == R.id.button1`, since you don't have to find the actual control and do a comparison. And you can use a `switch` instead of lots of ifs. – Sami Kuhmonen Dec 28 '14 at 18:02
  • Using xml android:onClick causes a crash. –  Oct 14 '19 at 04:53

17 Answers17

640

No, that is not possible via code. Android just implements the OnClickListener for you when you define the android:onClick="someMethod" attribute.

Those two code snippets are equal, just implemented in two different ways.

Code Implementation

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
}

Above is a code implementation of an OnClickListener. And this is the XML implementation.

XML Implementation

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

In the background, Android does nothing else than the Java code, calling your method on a click event.

Note that with the XML above, Android will look for the onClick method myFancyMethod() only in the current Activity. This is important to remember if you are using fragments, since even if you add the XML above using a fragment, Android will not look for the onClick method in the .java file of the fragment used to add the XML.

Another important thing I noticed. You mentioned you don't prefer anonymous methods. You meant to say you don't like anonymous classes.

Octavian Helm
  • 39,405
  • 19
  • 98
  • 102
  • 4
    I'm not a Java guru but yes, I meant anonymous class. Thanks for your reply. Very clear. – emitrax Nov 11 '10 at 13:54
  • 128
    Note that the if you use the XML onclick, you have to put the onclick method (`myFancyMethod()`) in the current Activity. This is important if you are using fragments, since the programmatic way of setting onclick listeners will probably have the method handling clicks in a fragment's onCreateView()... where it would **not** be found if referred to from XML. – Peter Ajtai Nov 12 '11 at 00:46
  • @PeterAjtai Thanks for pointing that out. You can add that to my answer if you want. Just edit it in. – Octavian Helm Nov 13 '11 at 15:55
  • does this method must be public ? – Mickey Tin Feb 05 '13 at 10:29
  • 12
    Yes, the method has to be public. – Octavian Helm Feb 05 '13 at 10:32
  • When you are accessing the same method for fragment related actions. Method should be declared in the activity..!! My exp – Gnanam R Feb 06 '13 at 08:53
  • 12
    Interesting thing is that doing it in code does allow one to shield method access by making the method private, whereas doing it the xml way leads to exposure of the method. – bgse Sep 21 '13 at 21:19
  • what if my method to be called doesn't take a View object as a parameter?how will I be able to call it from xml? – Romantic Electron May 21 '14 at 17:57
  • 5
    The fact that the function (in XML approach) must be in Activity is important not only when you consider fragments, but also custom views (containing the button). When you have a custom view which you reuse in multiple activities, but you want to use the same onClick method for all cases, the XML method is not the most convenient one. You would need to put this onClickMethod (with the same body) in every activity that uses your custom view. – Bartek Lipinski Sep 05 '14 at 17:12
  • this is really late but would it matter if you change the access modifier for the method? – committedandroider Sep 29 '14 at 06:55
  • I'm fairly sure that Android would complain but I'm not 100% sure anymore. It has been ages since I last did something Android related. – Octavian Helm Sep 29 '14 at 22:47
  • I found issues with using XML onClick for TextViews and Samsung phones. I had to back to the old school method in this answer of inlining – JPM Oct 22 '15 at 14:44
  • I found its usage in ViewPager, where one wants to disable the fling and instead implement *Next / Previous* buttons. Works flawless, the point worth noting is that you need to define it in the PagerActivity, where access to ViewPager object is easy. – Skynet Nov 24 '15 at 13:15
  • You can also use lambdas for Java 8+, I think. – Solomon Ucko Jan 27 '17 at 15:51
  • I created an `onLongClick="onLongClick_MainActivity"` custom attribute in my compound view. Works exactly like the `onClick` Android attribute (i.e. quite messy-feeling). – n00dles May 15 '19 at 03:01
93

When I saw the top answer, it made me realize that my problem was not putting the parameter (View v) on the fancy method:

public void myFancyMethod(View v) {}

When trying to access it from the xml, one should use

android:onClick="myFancyMethod"/>
starball
  • 20,030
  • 7
  • 43
  • 238
jp093121
  • 1,752
  • 19
  • 13
76

android:onClick is for API level 4 onwards, so if you're targeting < 1.6, then you can't use it.

Saro Taşciyan
  • 5,210
  • 5
  • 31
  • 50
James
  • 3,729
  • 3
  • 20
  • 16
34

Check if you forgot to put the method public!

Ruivo
  • 835
  • 8
  • 8
32

Specifying android:onClick attribute results in Button instance calling setOnClickListener internally. Hence there is absolutely no difference.

To have clear understanding, let us see how XML onClick attribute is handled by the framework.

When a layout file is inflated, all Views specified in it are instantiated. In this specific case, the Button instance is created using public Button (Context context, AttributeSet attrs, int defStyle) constructor. All of the attributes in the XML tag are read from the resource bundle and passed as AttributeSet to the constructor.

Button class is inherited from View class which results in View constructor being called, which takes care of setting the click call back handler via setOnClickListener.

The onClick attribute defined in attrs.xml, is referred in View.java as R.styleable.View_onClick.

Here is the code of View.java that does most of the work for you by calling setOnClickListener by itself.

 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;

As you can see, setOnClickListener is called to register the callback, as we do in our code. Only difference is it uses Java Reflection to invoke the callback method defined in our Activity.

Here are the reason for issues mentioned in other answers:

  • Callback method should be public : Since Java Class getMethod is used, only functions with public access specifier are searched for. Otherwise be ready to handle IllegalAccessException exception.
  • While using Button with onClick in Fragment, the callback should be defined in Activity : getContext().getClass().getMethod() call restricts the method search to the current context, which is Activity in case of Fragment. Hence method is searched within Activity class and not Fragment class.
  • Callback method should accept View parameter : Since Java Class getMethod searches for method which accepts View.class as parameter.
Manish Mulimani
  • 17,535
  • 2
  • 41
  • 60
  • 1
    That was the missing piece for me - Java uses Reflection to find the click handler starting with getContext(). It was a little mysterious to me how the click propagates up from a fragment to an Activity. – Andrew Queisser Jun 05 '14 at 23:40
17

There are very well answers here, but I want to add one line:

In android:onclick in XML, Android uses java reflection behind the scene to handle this.

And as explained here, reflection always slows down the performance. (especially on Dalvik VM). Registering onClickListener is a better way.

Vukašin Manojlović
  • 3,717
  • 3
  • 19
  • 31
Krupal Shah
  • 8,949
  • 11
  • 57
  • 93
  • 6
    How much can it slow the app down? :) Half a millisecond; not even? In comparison to actually inflating the layout it's like a feather and a whale – Konrad Morawski Sep 06 '16 at 09:17
15

Note that if you want to use the onClick XML feature, the corresponding method should have one parameter, whose type should match the XML object.

For example, a button will be linked to your method through its name string : android:onClick="MyFancyMethod" but the method declaration should show: ...MyFancyMethod(View v) {...

If you are trying to add this feature to a menu item, it will have the exact same syntax in the XML file but your method will be declared as: ...MyFancyMethod(MenuItem mi) {...

Antoine Lizée
  • 3,743
  • 1
  • 26
  • 36
6

Another way to set your on click listeners would be to use XML. Just add android:onClick attribute to your tag.

It is a good practice to use the xml attribute “onClick” over an anonymous Java class whenever possible.

First of all, lets have a look at the difference in code:

XML Attribute / onClick attribute

XML portion

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

Java portion

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

Anonymous Java Class / setOnClickListener

XML Portion

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

Java portion

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

Here are the benefits of using the XML attribute over an anonymous Java class:

  • With Anonymous Java class we always have to specify an id for our elements, but with XML attribute id can be omitted.
  • With Anonymous Java class we have to actively search for the element inside of the view (findViewById portion), but with the XML attribute Android does it for us.
  • Anonymous Java class requires at least 5 lines of code, as we can see, but with the XML attribute 3 lines of code is sufficient.
  • With Anonymous Java class we have to name of our method “onClick", but with the XML attribute we can add any name we want, which will dramatically help with the readability of our code.
  • Xml “onClick” attribute has been added by Google during the API level 4 release, which means that it is a bit more modern syntax and modern syntax is almost always better.

Of course, it is not always possible to use the Xml attribute, here are the reasons why we wouldn’t chose it:

  • If we are working with fragments. onClick attribute can only be added to an activity, so if we have a fragment, we would have to use an anonymous class.
  • If we would like to move the onClick listener to a separate class (maybe if it is very complicated and/or we would like to re-use it in different parts of our application), then we wouldn’t want to use the xml attribute either.
Bestin John
  • 1,765
  • 1
  • 20
  • 23
  • And please note that the function called using XML attributes should always be public and if they are declared as private hen it will result in exception. – Bestin John Jun 11 '16 at 14:33
5

By using the XML attribute you just need to define a method instead of a class so I was wondering if the same can be done via code and not in the XML layout.

Yes, You can make your fragment or activity implement View.OnClickListener

and when you initialize your new view objects in code you can simply do mView.setOnClickListener(this);

and this automatically sets all view objects in code to use the onClick(View v) method that your fragment or activity etc has.

to distinguish which view has called the onClick method, you can use a switch statement on the v.getId() method.

This answer is different from the one that says "No that is not possible via code"

Catarina Ferreira
  • 1,824
  • 5
  • 17
  • 26
CQM
  • 42,592
  • 75
  • 224
  • 366
5

With Java 8, you could probably use Method Reference to achieve what you want.

Assume this is your onClick event handler for a button.

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

Then, you pass onMyButtonClicked instance method reference in a setOnClickListener() call like this.

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

This will allow you to avoid explicitly defining an anonymous class by yourself. I must however emphasize that Java 8's Method Reference is actually just a syntactic sugar. It actually create an instance of the anonymous class for you (just like lambda expression did) hence similar caution as lambda-expression-style event handler was applied when you come to the unregistering of your event handler. This article explains it really nice.

PS. For those who curious about how can I really use Java 8 language feature in Android, it is a courtesy of retrolambda library.

onelaview
  • 1,121
  • 15
  • 17
4
   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;
    }}
jeet parmar
  • 868
  • 8
  • 19
3

Supporting Ruivo's answer, yes you have to declare method as "public" to be able to use in Android's XML onclick - I am developing an app targeting from API Level 8 (minSdk...) to 16 (targetSdk...).

I was declaring my method as private and it caused error, just declaring it as public works great.

Waqas Hasan
  • 98
  • 1
  • 9
  • it appears that variables declared in the hosting Activity's class cannot be used in the scope of the declared callback; the Bundle Activity.mBundle will throw an IllegalStateException/NullPointerException if used in myFancyMethod(). – Quasaur Feb 27 '13 at 15:53
3

Be careful, although android:onClick XML seems to be a convenient way to handle click, the setOnClickListener implementation do something additional than adding the onClickListener. Indeed, it put the view property clickable to true.

While it's might not be a problem on most Android implementations, according to the phone constructor, button is always default to clickable = true but other constructors on some phone model might have a default clickable = false on non Button views.

So setting the XML is not enough, you have to think all the time to add android:clickable="true" on non button, and if you have a device where the default is clickable = true and you forget even once to put this XML attribute, you won't notice the problem at runtime but will get the feedback on the market when it will be in the hands of your customers !

In addition, we can never be sure about how proguard will obfuscate and rename XML attributes and class method, so not 100% safe that they will never have a bug one day.

So if you never want to have trouble and never think about it, it's better to use setOnClickListener or libraries like ButterKnife with annotation @OnClick(R.id.button)

Livio
  • 700
  • 6
  • 9
1

Suppose, You want to add click event like this 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"/>

In java file, you have to write a method like this method.

public void register(View view) {
}
Andrey Korneyev
  • 26,353
  • 15
  • 70
  • 71
0

I am Write this code in xml file ...

<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"/>

And write this code in fragment...

public void register(View view) {
}
0

The best way to do this is with the following code:

 Button button = (Button)findViewById(R.id.btn_register);
 button.setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View v) {
                //do your fancy method
            }
        });
  • I don't see how this answers the question - the asker wants to avoid creating an anonymous class, and instead use a class method. – ajshort Feb 15 '16 at 01:16
0

To make your life easier and avoid the Anonymous Class in setOnClicklistener (), implement a View.OnClicklistener Interface as below:

public class YourClass extends CommonActivity implements View.OnClickListener, ...

this avoids:

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

and goes directly to:

@Override
public void onClick(View v) {
  switch (v.getId()) {
    case R.id.your_view:
      yourMethod();
      break;
  }
}