What is the right way to communicate from a custom View to the Activity in which it resides?
Solution 1
the right way to do that, is to "listen" to your custom view by exposing an interface which your view holding a reference to instance of him, and you hosting activity should implement. exactly like the OnItemSelected interface and any events which android views are exposing is been implemented. this is the observer design pattern.
for example:
public class MyCustomSpinner extends Spinner {
public MyCustomSpinner(Context context) {
super(context);
// TODO Auto-generated constructor stub
}
public interface IMyEventListener {
public void onEventOccurred();
}
private IMyEventListener mEventListener;
public void setEventListener(IMyEventListener mEventListener) {
this.mEventListener = mEventListener;
}
protected void someMethodWhichDoingSomthingAndShouldRaiseAlsoTheEvent() {
/*
* Some Code which the function doing //more code...
*/
if (mEventListener != null) {
mEventListener.onEventOccurred();
}
}
}
this is how you will use it from your activity:
mMyCustomSpinner.setEventListener(new IMyEventListener() {
@Override
public void onEventOccurred() {
// TODO Auto-generated method stub
}
});
Solution 2
I'm trying to figure out what the correct way to talk to the Activity that it's embedded in is, when the user makes a selection.
You don't want to "talk to the Activity that it's embedded in". You want to talk to the controller responsible for the View
. Today, that might be an Activity
. Tomorrow, that might be a Fragment
.
I see that the OnItemSelected listener gets a reference to the Adapter, but I'm not clear on whether or not I should be using this adapter and walking up its parent chain somehow
That implies that the View
knows the specific type of Adapter
, since the Adapter
interface does not have any sort of getContext()
method. Moreover, it ties you to talking to the Activity
, which is not a good plan at this point, as mentioned above.
Personally, I'm a bit dubious about having a custom Spinner
subclass in the first place. But, assuming there's a good reason for it, you should follow Tal Kanel's advice (posted while I was writing this) and design a custom listener interface for this custom event that is being declared by your custom View
. Have the controller (Activity
or Fragment
) supply an implementation of that interface -- this could be directly implemented on the controller, or implemented as an anonymous inner class (as in Tal Kanel's answer), etc. Have your custom View
call method(s) on the listener interface as needed.
Solution 3
A simple solution -
((ParentClass) context).functionToRun();
Where ParentClass is the class name of the activity.
Solution 4
The correct way is using a listener of some sort. I think you can make direct reference, your code would just not be reusable for another project then...
![Yevgeny Simkin](https://i.stack.imgur.com/veKXw.jpg?s=256&g=1)
Yevgeny Simkin
I fled Soviet Russia as a child and have spent my life bouncing from music to comedy to software engineering. You can follow my comedy Twitter feed here. I'm also the founder and CEO of The Russian Mob™—an agency specializing in developing SAAS, Mobile, AR, VR, and Web applications. (No: we won’t help you hack a foreign election so don’t bother asking.) On occasion, things that I think are published over at The Bulwark, which you should be reading, even if my ideas weren't being published there. If you would like to connect with me, the easiest way is through LinkedIn.
Updated on June 03, 2022Comments
-
Yevgeny Simkin about 2 years
I have a custom View class that extends Spinner. I'm trying to figure out what the correct way to talk to the Activity that it's embedded in is, when the user makes a selection. I see that the
OnItemSelected
listener gets a reference to the Adapter, but I'm not clear on whether or not I should be using this adapter and walking up its parent chain somehow, or if I should just talk directly to the context (for some reason that doesn't feel safe, even though I can't think of a way in which it might fail, offhand). -
Yevgeny Simkin about 12 yearsas I said, I *have a listener... it's the Spinner.OnItemSelectedListener. When the user selects something I'd like to do some stuff in the parent Activity. I am crafting a custom Listener IN the Activity (precisely because I'd like it to be reusable) and passing that Listener into my custom Spinner. The point I'm not entirely clear on is how to have a generic Spinner that I can dynamically have communicate with whatever Activity it happens to be in.
-
Squonk about 12 years"When the user selects something I'd like to do some stuff in the parent Activity" - It isn't the responsibility of a 'view' or any other UI element to control an
Activity
. The 'view' should never (or rarely) know about its 'parents' (to use a generic term). It's the responsibility for theActivity
to know what IT wants to do when a 'listener' callback method is called and to act accordingly. -
Yevgeny Simkin about 12 years@MisterSquank, we're talking about the same thing. When a user alters a drop down, I needs things to happen in the Activity that drop down is in. I am using the OnItemSelectedListener to invoke this. Are you suggesting that there's a better way for the Activity to know that someone has done something to the drop down?
-
Yevgeny Simkin about 12 yearsmaybe I'm not being clear. It is precisely the fact that I want to make it ambiguous *what the Spinner is a child of that I'm asking this question. As it happens the reason for my custom Spinner is that I think the Android Spinner is totally useless because since it ONLY has an index and a string it always requires a big bunch of messy code to go along side it if the selected item needs to have an accompanying Object of some sort (in the HTML world this is the "data". Why the devs at Google overlooked this feature, I can't say (or am I missing it somehow?)
-
Yevgeny Simkin about 12 yearsSo, what I need is a way to add the Listener to the Spinner after it's resident in the (currently) Activity and give it the ability to call back out to the Activity when the user selects something, at which point the Activity will take over and do whatever it needs to do with the selected item. So, given that I'm building this Listener IN the Activity, I think I can just make a Context reference and use that? right? or is this not recommended for some reason?
-
CommonsWare about 12 years@Dr.Dredel: "Android Spinner is totally useless because since it ONLY has an index and a string" -- an
AdapterView
has an index and anAdapter
. TheAdapter
adaptsObject
, notString
. You can have anAdapter
adaptingString
, of course, orVideo
,Restaurant
,ResolveInfo
, or whatever.AdapterView
(of whichSpinner
is one) has nothing to do withString
. -
CommonsWare about 12 years@Dr.Dredel: "or is this not recommended for some reason?" -- that is the crux of my answer. It is echoed by Tal Kanel's answer and MisterSquonk's comment. At this point, you have three people telling you to create a custom listener interface for your custom event for your custom
View
. Whether you implement this listener interface on theActivity
or on something else theActivity
hands to the customView
is up to you. -
Yevgeny Simkin about 12 yearsthanks. I understand what you're saying, and will do so. As to your comment on the Adapter, I'm confused. Are you saying that the native Spinner is prepared to deal with something other than the Strings it displays and I can utilize this to have it hang onto not just said String but also an accompanying object? Cause I'm not seeing how to do that at all.
-
CommonsWare about 12 years@Dr.Dredel: "Are you saying that the native Spinner is prepared to deal with something other than the Strings it displays" --
Spinner
does not displayString
s. It never did and never will. It displaysView
s. ThoseView
s come from anAdapter
. What theView
s are and what model data they come from is up to theAdapter
. You can create anArrayAdapter<Bitmap>
, use that for yourSpinner
, and never touch aString
orTextView
(using insteadImageView
for the views created bygetView()
in theArrayAdapter
). -
Yevgeny Simkin about 12 yearsI understand. Ok, I'll consider it. Offhand it looks like a lot more work to create a custom Adapter than a custom Spinner (and I'm not entirely clear on what the advantage would be), but I'll see if that makes more sense.
-
CommonsWare about 12 years@Dr.Dredel: "Offhand it looks like a lot more work to create a custom Adapter than a custom Spinner" -- it should be less work. Moreover, it is far less fragile (composition over inheritance). But, most importantly, it is how
AdapterView
is supposed to work. I have never seen an example, anywhere, where somebody would subclass something likeSpinner
to avoid creating anAdapter
. -
Yevgeny Simkin about 12 yearsI'm sure you're correct. Unfortunately this boils down to the old "it's the only tool I have" problem. Looking at the Adapter approach I am stumped as to how to give a native Spinner what it wants so that it displays the labels in its listed Views all the while tacking on an additional Object to each one. It may be totally simple (probably is) but absent a tutorial, I'm left to figure this out on my own, and I simply don't have the bandwidth in my schedule to do so. Do any of your books cover this topic? :)
-
CommonsWare about 12 years@Dr.Dredel: All of them do, to varying degrees. Most examples will be for
ListView
, as it is more popular, though the concept still holds forSpinner
. Grab a free older edition of one of my books and read the "Getting Fancy With Lists" chapter for starters. Then, grab a free older edition of one of my other books and work through tutorials #1-#5. -
Sojurn over 10 yearsIsn't this setting up a circular reference which will leak ?
-
Tal Kanel over 10 years@Sojurn: the observer pattern is not perfect. actually - nothing is not perfect, but in the Java world (and android) it's the best solution so far. "can it cause memory leak?" - yes it can, but only if you are not using it properly. for example - if you implement your listener outside the view's hosting activity - it can cause leak if not removing the listener at the right time in the activity life - cycle. usually, when you want to react to view events - you would implement the callback within the activity, so you don't need to worry about that.
-
tronman about 6 yearsIt's a simple solution but should be avoided since it couples the view to it's parent. Ideally you want the view to be reusable and independent of whatever activity/fragment uses the view.