Reputation: 73
There are four ways to add an onClickListener to a clickable View (button, for example):
So my question is, how do you choose one of these implementation techniques over another? Is there a best practices according to certain conditions, or is it just a matter of programmer preference?
Upvotes: 6
Views: 2938
Reputation: 1827
There is a four ways to use OnClickListener
.
To define OnClickListener
within the method call site.
public class MainActivity extends AppCompatActivity {
@Override
protected void onCreate(@Nullable Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
Button button = findViewById(R.id.myButton);
button.setOnClickListener(new OnClickListener() {
@Override
public void onClick(View v) {
// do something
}
});
}
}
First reasons why to avoid this is because it clutters up onCreate
method. This becomes even more apparent when you want to observe click events from multiple views.
The next reason to avoid this is because it doesn't promote code reuse if several buttons should do the same.
The second way is almost the same as first except implementation to field is assigned in the class.
public class MainActivity extends AppCompatActivity {
private View.OnClickListener clickListener = new View.OnClickListener() {
@Override
public void onClick(View v) {
// do something
}
};
@Override
protected void onCreate(@Nullable Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
Button button = findViewById(R.id.myButton);
button.setOnClickListener(clickListener);
}
}
This way is pretty the same as first one, only advantage is that the method could be reused for several buttons.
This way is to declare an inner class to implement OnClickListener
. If it will be used multiple times is better to define the instance as a field.
public class MainActivity extends AppCompatActivity {
@Override
protected void onCreate(@Nullable Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
Button button = findViewById(R.id.myButton);
button.setOnClickListener(new ButtonClick());
}
class ButtonClick implements View.OnClickListener {
@Override
public void onClick(View v) {
// do something
}
}
}
The advantage of this way is that it helps to organize the code. You can easily collapse this internal class and forget about it until you need to look at it.
The other good reason is that it could be turned in public class and reused in other app areas.
The fourth way involves Activity to implement OnClickListener
.
public class MainActivity extends AppCompatActivity implements View.OnClickListener {
@Override
protected void onCreate(@Nullable Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
Button button = findViewById(R.id.myButton);
button.setOnClickListener(this);
}
@Override
public void onClick(View v) {
// do something
}
}
The first disadvantage that this way creates a public method in the Activity and there you should pass this
activity when setOnClickListener
is called.
Second reason to avoid this way is that if another button is added you should determine which button was clicked. Then you should use switch()
or if()
statements. It isn't performed because it wastes a cycle or several for each button click.
Last disadvantage for this way is that difficult to organize a class. In example you have an Activity which implements multiple interfaces. Suddenly all of the methods from these interfaces are intertwined together, this becomes more evident after you’ve added methods to some of those interfaces. Also now you can't add an interface with method named onClick
.
There is some differences between these ways, but you should choose your way according your code and needs
Upvotes: 2
Reputation: 1489
Here we use so called callback pattern.
public class Button {
private Callback callback;
public Button(Callback callback) {
this.callback = callback;
}
public void update() {
// Check if clicked..
callback.onClick(this);
}
public interface Callback {
public void onClick(Button Button);
}
}
Button b = new Button(new Callback() {
@Override
public void onClick(Button b) {
System.out.println("Clicked");
}
});
In our case onClick handler implements the interface View.OnClickListener.
Key points:
1) Attribute in the XML file can be used only for activity, as @Karakuri mentioned it uses reflection which is slow.
2) Anonymous inner class has special rules for access to the members of enclosing class (check [1], [2]). There are some situations when memory leaks can happen (ex. threading with AsyncTask, Handlers).
3) Here you have a full access to the members of enclosing class.
4) Is a variation of 3d.
Readability depends on your handler size, small logic can be ok to inline, but for larger blocks of code consider 3d and 4th.
Upvotes: 4
Reputation: 38595
I never use the onClick
attribute as it ties the layout to a specific Activity (it must find the method via reflection). It doesn't work on Fragments.
Options 2 and 3 are virtually identical. Option 3 might be more advantageous if you want to use the private member as the OnClickListener
of multiple views.
Option 4 is close to option 3. One key difference is that it changes the class declaration, so if it's important to you to keep the class declaration free of interface implementations (or perhaps you need to maintain binary compatibility of some kind), you may not want to use this option.
My advice is to avoid option 1 and choose whichever fits your code style best. You also aren't required to use the same approach in every single place in your code.
Upvotes: 2