I have a question that seems simple but I cannot figure out what is the best practice for that :)
What is the best practice to access from a View, a method on the Activity that launched the View?
For example, I have an Activity with a layout that contains a Button and a Textfield. I want when I click on the Button, to call a method on my Activity that update the Textfield with some value. I come with multiple solutions:
1 - Inner class for the OnClickListener directly on the Activity so I can the method of the Activity with MyActivity.this.updateTextField() on onClick method
2 - Outer class for the OnClickListener, on my onClick method I can do: ((MyActivity)getContext()).updateTextField()
3 - Reference the Activity on my OnClickListener class when I instantiate it:
myButton.setOnClickListener(new MyOnclickListener(MyActivity));
I don´t want solution 1 because I don´t like that much inner class and I want reusable code. Solution 2 seems good but can produce error on runtime if my context is not an activity. Solution 3 seems good also but "heavy".
What is the best practice on Android to tell from the View to its Actitity that something needs to be done on the Activity?
Thanks!
implement activity with onclickListener and add unimplemented method onclick
just check for the view to see which button is clicked incase you are using multiple buttons
Although I mostly find myself end up with inner classes, there are other options.
You can create an interface like the following and let your activity implement it:
public interface UpdateableTextField {
public void updateTextField();
}
Now let the Activities that you want implement this interface.
Now, create a class that implements View.OnclickListener and set the constructor to get UpdateableTextField as a parameter:
public class MyListener implements View.OnclickListener {
UpdateableTextField updatable;
public MyListener(UpdateableTextField updatable) {
this.updateable = updatable;
}
#Override public void onClick(View v) {
// do some stuff
updateable.updateTextField();
}
}
And last, in the Activity:
public class MyActivity extends Activity implementes UpdateableTextField{
#Override public void onCreate(Bundle savedInstanceState) {
// usuall stuff
MyListener listener = new MyListener(this);
someView.setOnClickListener(listener);
// other stuff
}
#Override public void updateTextField() {
// well, update the text field :)
}
}
Related
I have only one activity in my app. Before I just stored my views and dialogs static in the activity, so I could access them from anywhere. But I know that this is bad practice because it leads to memory leaks.
So I made them non-static, but now I need to have a reference to my activity deep down in my view hierarchy, to access the views and dialogs stored in the activity.
Example:
My MainActivity has a dialog called a and a custom view called b. How can the onClick method of b show the dialog a?
or in code:
public class MainActivity extends Activity {
private CustomDialog a;
private CustomView b;
#Override
protected void onCreate(Bundle savedInstanceState) {
a = new CustomDialog(this);
b = new CustomView(this);
}
}
public class CustomView extends Button implements OnClickListener {
public CustomView(Context context) {
super(context);
setOnClickListener(this);
}
#Override
public void onClick(View view) {
//wants to show dialog a
MainActivity.a.show(); //Not possible -> a is not static
mainActivity.a.show(); //<-- needs a reference of the activity
// but where from?
}
}
MainActivity mainActivity = (MainActivity) getContext(); won't work because getContext() is not always an activity context.
UPDATE:
I posted an answer below!
For some reasons StackOverflow only lets me accept my own answer in two days
I do not know what exactly your view hierarchy looks like.
I picture your problem for example as:
Activity A has a recyclerview R, now every viewholder H in R should be able to trigger some method in A.
In such a scenario it would be feasable to pass a reference of your activity to your recyclerview adapter and then the adapter passes it to the ViewHolder.
Which then uses it in the onClick method of your (viewholder's) view.
Here, you could use the "callback" pattern. There are many posts about this on stackoverflow, e.g. here.
So the implementation steps would be:
define interface
let your activity implement that interface
let your adapter take the interface as a constructor parameter and pass your activity. (in this example: you have to repeat the step with your viewHolder, pass the interface from the adapter)
use this interfaces method in the onClick method -> this will then trigger your activities method
The implementation depends on the actual hierarchy. If your other view is in a fragment, then you could also use a (shared) ViewModel.
According to your picture I was thinking of the callback-pattern approach first.
You could override onClick in MainActivity; there is probably no need to do it in the class definition itself.
public class MainActivity extends Activity {
private CustomDialog a;
private CustomView b;
#Override
protected void onCreate(Bundle savedInstanceState) {
a = new CustomDialog(this);
b = new CustomView(this);
b.setOnClickListener(new View.OnClickListener() {
#Override
public void onClick(View v) {
a.show();
}
});
}
}
This is a very common pattern in android and I don't know what your view hierarchy looks like but it should work in most cases.
I am having trouble understanding why any class extending Button would need to implement View.OnClickListener. It makes much more sense to create listeners in activities or have MainActivity implement OnClickListener.
A few minutes a go there was an answer here that turned out to be correct.
I don't know why the author deleted it, but it had a link to this answer:
private static Activity unwrap(Context context) {
while (!(context instanceof Activity) && context instanceof ContextWrapper) {
context = ((ContextWrapper) context).getBaseContext();
}
return (Activity) context;
}
So everytime you need the activity you just can call Activity activity = unwrap(getContext());.
I don't know if it is really intended to do it that way or if it is a workaround, but it does its job (atleast in my case).
I'm having a problem having a switch button listener to work correctly. It is in my main activity as is:
public class MainActivity extends WearableActivity {
private Switch mySwitch;
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
// Enables Always-on
setAmbientEnabled();
// React to settings change
mySwitch= findViewById(R.id.mySwitch);
mySwitch.setOnCheckedChangeListener(this); // <--------
}
public void onCheckedChange(CompoundButton s, boolean isChecked) {
...
}
}
At the arrow line, I tried with this (like a sample on the internet). It is marked in Android Studio as an error. AS suggests me to cast to a CommpoundButton.onCheckedChange (or similar). When I start the app, it crashs saying MainActivity cannot be casted to that. I cannot do also setOnCheckedChangeListener(onCheckedChange);
What I am doing wrong?
You need to implement OnCheckedChangeListener if you want to use this as a option for setOnCheckedChangeListener().
public class MainActivity extends WearableActivity implements CompoundButton.OnCheckedChangeListener{
This is because setOnCheckedChangeListener() will only accept an instance of OnCheckedChangeListener, so you can't simply use this since the Activity that this is pointing to isn't an instance of OnCheckedChangeListener.
However, since OnCheckedChangeListener is an interface, this is easily rectified by implementing OnCheckedChangeListener. Thanks to how inheritance works in Java, Activity automatically becomes an instance of OnCheckedChangeListener once the interface has been implemented.
It looks like this was the option you were going for since you're also overriding the onCheckedChange() method inside your Activity.
I know that every situation will be different, but I just wanted see if there was a general recommendation.
Currently, I have my activities (screens) dynamically creating custom button objects and custom edit text objects. Each of these objects have listeners to see if their state has changed. These object classes have all the logic for the screen. The activity's only job is to assign objects to the widgets I created in XML.
Part of me thinks it should be opposite, where the activity contains all the logic for all the widgets on the screen and simply waits for the objects to notify it when the listeners go off.
Which way is more "standard" ?
I use the following way. I have a common EventHandler sub class in every activity or fragment and I add a single instance belonging to activity to each UI item. EventHandler implements OnClickListener, OnChanged.., and so on.
I would also recommend looking at this library, if you are familiar with DI concept:
https://github.com/roboguice/roboguice/
Here is an example of code of mine:
package com.x.y;
public class DashboardActivity extends FragmentActivity {
private EventHandler eventHandler = new EventHandler();
#SomeAnnotationForInit(R.id.some_id)
private Button feedButton;
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.dashboard_activity);
initGui();
}
private void initGui() {
feedButton.setOnClickListener(eventHandler);
}
private class EventHandler implements View.OnClickListener {
#Override
public void onClick(View view) {
if(view.equals(...)) {
//TODO:
}
}
}
}
There are multiple ways to register callbacks when a Button is clicked. If I go by the following way:
public class MainActivity extends Activity implements OnClickListener {
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
Button button = (Button) findViewById(R.id.button1);
button.setOnClickListener(this);
}
#Override
public void onClick(View v) {
Toast.makeText(this, "Hello onCLick", Toast.LENGTH_SHORT).show();
}
}
I don't understand how the method setOnClickListener(this) identifies that it should call onClick() method?
This refers to the activity. Because the Activity implements an OnClickListener calling button.setOnClickListener(this) gives the onClickListener that the Activity implements to setOnClickListener.
I recommend you look up info about implementing interfaces in Java if you want tot know more about this practise.
if you are aware about oops 'this' refer the reference of current object of class. a good explanation is define here
In above case MainActivity reference is refer as this here.
public void setOnClickListener(OnClickListener l)
is setter method define in class Button which hold the reference on "OnClickListener".
when you set setOnClickListener(this) it define you are passing OnClickListener reference as your activity so to make your activity as type on OnClickListener you have to implement the interface OnClickListener in your activity class as it is showing in your code.
public class MainActivity extends Activity implements OnClickListener
Its a callback listener which have method "onClick" you have to override that method
and when button is clicked that method is call by Button class so the event listener (which is you activity in current scenario) can listen to it.
I think I understand your confusion. When you read other SO answers or references like the View.OnClickListener, it feels like all the sentences are telling the same thing but nothing that really helps click.
What happens is, when a Button is clicked, it will notify all the objects that are listenning for it. You are subscribing your activity as a listener, to this event with the line
button.setOnClickListener(this);
So, on an event of a click, button knows that it should call the activity's onClick event.
I don't understand how the method setOnClickListener(this) identifies
that it should call onClick() method?
(Therefore, it s the button that calls the listener.onClick() method, in case there's a confusion there.)
Also, #nourikhalass has a point, you should first make sure that interfaces make sense to you.
Is it any clearer?
Your code has
MainActivity implements OnClickListener
but actually it is:
MainActivity implements View.OnClickListener
Maybe that is what confuses you.
"This" refers to current object.
To handle button clicks, an object must implement the "OnClickListener" interface and define what to do when clicks are received in "onClick" method. Then you can register that object as a listener for your button clicks.
In your case, your activity implements OnClickListener, and onClick shows a toast:
public class MainActivity extends Activity implements OnClickListener {
...
#Override
public void onClick(View v) {
Toast.makeText(this, "Hello onCLick", Toast.LENGTH_SHORT).show();
}
Therefore, your activity can handle button clicks, so you register it as a listener for your button:
button.setOnClickListener(this);
As "this" implements the required interface, is a valid listener.
I have a title bar in my application. And the title bar has one button. On click of that button I display info activity. Now, as far as I know, android:onClick needs a reference of a public method inside the activity which has the xml set in setContentView(). Now, as the logic for that buttons click will be the same throughout the application, so what I want is, I will the method just once say showInfoScreen(View view) and put it in that buttons onClick attribute. And I need not write the same method everywhere. Is it possible?
Of course it's possible. Write an Activity class, then have all of your Activitys extend it. For example:
public abstract class BaseActivity extends Activity {
#Override
public void setContentView( int layoutResID ) {
super.setContentView( layoutResID );
findViewById(R.id.button).setOnClickListener(new OnTitleBarButtonClickListener());
}
private void showInfoScreen() {
// Show the info screen
}
private class OnTitleBarButtonClickListener implements OnClickListener {
#Override
public void onClick(View v) {
showInfoScreen();
}
}
}
Then all of your derived Activitys would extend BaseActivity instead of Activity.
The beauty of doing it this way is that any Activity that extends this class automatically gets this feature. No coding is required in the derived classes, just in BaseActivity. The only contract all of your Activitys will have will be to have R.id.button or whatever id you name it within its content.
I think you have to write onclick in every Activity where you want to display infoscreen.
But OnClick you just call A method showInfoScreen(View view) in every Activity....
And you should create class Like...ShowInfo and there are one static method...
public class ShowInfo{
public static void showInfoScreen(View view,Context c){
//now dispay info here
}
}
Write ShowInfo.showInfoScreen(v,YourClassName.this) in your onClick() Method....
An example of what Vinayak.B suggested is like this:
public class yourAppUtils {
public static void yourMethod() {
// Do stuff
}
}