What is the right way to communicate from a custom View to the Activity in which it resides?

11,107

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

Share:
11,107
Yevgeny Simkin
Author by

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, 2022

Comments

  • Yevgeny Simkin
    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
    Yevgeny Simkin about 12 years
    as 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
    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 the Activity to know what IT wants to do when a 'listener' callback method is called and to act accordingly.
  • Yevgeny Simkin
    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
    Yevgeny Simkin about 12 years
    maybe 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
    Yevgeny Simkin about 12 years
    So, 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
    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 an Adapter. The Adapter adapts Object, not String. You can have an Adapter adapting String, of course, or Video, Restaurant, ResolveInfo, or whatever. AdapterView (of which Spinner is one) has nothing to do with String.
  • CommonsWare
    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 the Activity or on something else the Activity hands to the custom View is up to you.
  • Yevgeny Simkin
    Yevgeny Simkin about 12 years
    thanks. 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
    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 display Strings. It never did and never will. It displays Views. Those Views come from an Adapter. What the Views are and what model data they come from is up to the Adapter. You can create an ArrayAdapter<Bitmap>, use that for your Spinner, and never touch a String or TextView (using instead ImageView for the views created by getView() in the ArrayAdapter).
  • Yevgeny Simkin
    Yevgeny Simkin about 12 years
    I 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
    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 like Spinner to avoid creating an Adapter.
  • Yevgeny Simkin
    Yevgeny Simkin about 12 years
    I'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
    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 for Spinner. 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
    Sojurn over 10 years
    Isn't this setting up a circular reference which will leak ?
  • Tal Kanel
    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
    tronman about 6 years
    It'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.