Reputation: 4585
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.
Upvotes: 451
Views: 455958
Reputation: 1762
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"/>
Upvotes: 93
Reputation: 99
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;
}
}
Upvotes: 0
Reputation: 9187
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.
Upvotes: 18
Reputation: 710
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)
Upvotes: 3
Reputation: 44308
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"
Upvotes: 5
Reputation: 39603
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.
Upvotes: 645
Reputation: 1803
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:
Of course, it is not always possible to use the Xml attribute, here are the reasons why we wouldn’t chose it:
Upvotes: 6
Reputation: 1133
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.
Upvotes: 5
Reputation: 1
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
}
});
Upvotes: 0
Reputation: 11
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) {
}
Upvotes: 0
Reputation: 868
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;
}}
Upvotes: 4
Reputation: 11
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) {
}
Upvotes: 1
Reputation: 17625
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:
Java Class getMethod
is used, only functions with public access specifier are searched for. Otherwise be ready to handle IllegalAccessException
exception.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.Java Class getMethod
searches for method which accepts View.class
as parameter.Upvotes: 32
Reputation: 3749
android:onClick
is for API level 4 onwards, so if you're targeting < 1.6, then you can't use it.
Upvotes: 75
Reputation: 3943
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) {...
Upvotes: 15
Reputation: 98
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.
Upvotes: 3