453

How can you set the event listener for a Spinner when the selected item changes?

Basically what I am trying to do is something similar to this:

spinner1.onSelectionChange = handleSelectionChange;

void handleSelectionChange(Object sender){
    //handle event
}
Vadim Kotov
  • 8,084
  • 8
  • 48
  • 62
Soni Ali
  • 18,464
  • 16
  • 44
  • 53

17 Answers17

900

Some of the previous answers are not correct. They work for other widgets and views, but the documentation for the Spinner widget clearly states:

A spinner does not support item click events. Calling this method will raise an exception.

Better use OnItemSelectedListener() instead:

spinner.setOnItemSelectedListener(new OnItemSelectedListener() {
    @Override
    public void onItemSelected(AdapterView<?> parentView, View selectedItemView, int position, long id) {
        // your code here
    }

    @Override
    public void onNothingSelected(AdapterView<?> parentView) {
        // your code here
    }

});

This works for me.

Note that onItemSelected method is also invoked when the view is being build, so you can consider putting it inside onCreate() method call.

Shine
  • 3,788
  • 1
  • 36
  • 59
znq
  • 44,613
  • 41
  • 116
  • 144
  • 53
    the problem with this is that the onItemSelected method is also invoked when the view is being build. So code that is written in there gets executed on startup as well. Is there a way of executing the containing code only if there is a real item selection invoked by the user? – Kennethvr Dec 28 '10 at 11:53
  • 40
    actually that problem can be solved by putting the setOnItemSelectedListener in the override OnStart method and not in the onCreate method. stupid me... – Kennethvr Dec 28 '10 at 12:05
  • 17
    I have put the listener in the onStart method, but it's called before the user ever gets to see anything, just like onCreate is, so, in my case where a "proceed" button that is meant to be invisible until the user selects something, the button is made visible upon initial display. Are you saying your experience is different? If so, what are you doing differently in the onStart method that I'm missing? – Yevgeny Simkin Feb 28 '11 at 18:47
  • 7
    Use another field inside your anonymous listener to record the first selection, and tell onItemSelected not to do anything unless a selection has been encountered? Just a thought. – Sam Svenbjorgchristiensensen Mar 21 '11 at 09:34
  • 4
    But what if the user selects the "default" item, the one up the top? Then `onItemSelected(...)` is not hit. (I know because I just found this out the hard way.) – Andrew Wyld Mar 23 '12 at 16:40
  • the problem of onItemSelected being invoked upon Activity creation is also discussed on http://stackoverflow.com/questions/2562248/android-how-to-keep-onitemselected-from-firing-off-on-a-newly-instantiated-spin – Shine Aug 09 '12 at 22:02
  • 1
    I would suggest taking advantage of setOnItemSelectedListener being called during construction. Use that as an opportunity to initialize the spinner. Also what I've seen is that the listener is called whether it's added in onStart or onCreate. – pmont Aug 03 '13 at 23:47
  • One more way could be to introduce a default option like "Please select " on top of the array , and introduce a condition in the onitemselected , that is told not to do anything if the selectedview.gettext returns "Please Select" . – Muhammad Ahmed AbuTalib Nov 14 '13 at 07:44
  • 2
    I set a class variable int to the number of spinner listeners I will be adding in onCreate and decrement it as each onItemSelected is invoked. Untill it reaches zero they just return. – Steve Waring Jul 20 '14 at 22:14
  • Is it okay? if i don't put any code within `public void onNothingSelected` . – CODAR747 Jul 31 '20 at 07:49
  • to avoid invoking when the view is being build use spinner.setSelection(0, false); – Elfnan Sherif Sep 21 '22 at 10:13
63
Spinner spnLocale = (Spinner)findViewById(R.id.spnLocale);

spnLocale.setOnItemSelectedListener(new AdapterView.OnItemSelectedListener() {
    public void onItemSelected(AdapterView<?> adapterView, View view, int i, long l) { 
        // Your code here
    } 

    public void onNothingSelected(AdapterView<?> adapterView) {
        return;
    } 
}); 

Note: Remember one thing.

Spinner OnItemSelectedListener event will execute twice:

  1. Spinner initialization
  2. User selected manually

Try to differentiate those two by using flag variable.

Vasily Kabunov
  • 6,511
  • 13
  • 49
  • 53
Santhosh
  • 1,357
  • 4
  • 15
  • 30
  • 22
    Just set a global Boolean like Boolean initialDisplay = true; And then in your onSelect see if it's true and if it is, don't do whatever else you were going to do on select but set the flag to false, for the next time it's called (when the user actually selects). – Yevgeny Simkin Feb 28 '11 at 19:41
  • Genia S. method doesn't work realy well, because it's true that the first time that will be fired onselect event(on onCreate method), you can exclude this event by global var but if then i need to select the already selected item on spinner(the default item, the first) it will not be fired. I have tried to put a spinner label and exclude it, but it will be visible on Dialog opened. I agree with @Batdude. The Spinner is one screwed up widget. – jedi Nov 28 '13 at 15:08
  • Also note that if you use Genia S's method and you have multiple spinners using the same `onItemSelectedListener`, you'll have to either track the initialDisplay of each spinner separately, or count the initial calls until you reach the number of spinners. :-p – LarsH Jul 25 '19 at 14:35
25

You can implement AdapterView.OnItemSelectedListener class in your Activity.

And then use the below line within onCreate()

Spinner spin = (Spinner) findViewById(R.id.spinner);
spin.setOnItemSelectedListener(this);

Then override these two methods:

public void onItemSelected(AdapterView<?> parent, View v, int position, long id) {
    selection.setText(items[position]);
}

public void onNothingSelected(AdapterView<?> parent) {
    selection.setText("");
}
dakshbhatt21
  • 3,558
  • 3
  • 31
  • 40
Dhasneem
  • 4,037
  • 4
  • 33
  • 47
21

https://stackoverflow.com/q/1714426/811625

You can avoid the OnItemSelectedListener() being called with a simple check: Store the current selection index in an integer variable and check within the onItemSelected(..) before doing anything.

E.g:

Spinner spnLocale;

spnLocale = (Spinner)findViewById(R.id.spnLocale);

int iCurrentSelection = spnLocale.getSelectedItemPosition();

spnLocale.setOnItemSelectedListener(new AdapterView.OnItemSelectedListener() {
    public void onItemSelected(AdapterView<?> adapterView, View view, int i, long l) { 
    if (iCurrentSelection != i){
            // Your code here
    }
    iCurrentSelection = i;
    } 

    public void onNothingSelected(AdapterView<?> adapterView) {
        return;
    } 
}); 

Of cause the iCurrentSelection should be in object scope for this to work!

Community
  • 1
  • 1
Sampath
  • 1,144
  • 1
  • 21
  • 38
  • 1
    You can't use a non-final variable within an anonymous inner class. If the `iCurrentSelection` variable is declared within this anonymous class it will work fine. You could initialise it to -1 so the code gets executed on the first call. – dahvyd May 19 '12 at 06:44
12

It doesn't matter will you set OnItemSelectedListener in onCreate or onStart - it will still be called during of Activity creation or start (respectively).
So we can set it in onCreate (and NOT in onStart!).
Just add a flag to figure out first initialisation:

private Spinner mSpinner;
private boolean mSpinnerInitialized;

then in onCreate (or onCreateView) just:

mSpinner.setOnItemSelectedListener(new AdapterView.OnItemSelectedListener() {
            public void onItemSelected(AdapterView<?> adapterView, View view, int i, long l) {
                if (!mSpinnerInitialized) {
                    mSpinnerInitialized = true;
                    return;
                }

                // do stuff
            }

            public void onNothingSelected(AdapterView<?> adapterView) {
                return;
            }
        });
Leo DroidCoder
  • 14,527
  • 4
  • 62
  • 54
9

Find your spinner name and find id then implement this method.

spinnername.setOnItemSelectedListener(new OnItemSelectedListener() {

    @Override
    public void onItemSelected(AdapterView<?> parentView, View selectedItemView, int position, long id) {
        // your code here
    }

    @Override
    public void onNothingSelected(AdapterView<?> parentView) {
        // your code here
    }
});
dakshbhatt21
  • 3,558
  • 3
  • 31
  • 40
droidster.me
  • 558
  • 8
  • 16
8
spinner1.setOnItemSelectedListener(
    new AdapterView.OnItemSelectedListener() {
        //add some code here
    }
);
Guido
  • 46,642
  • 28
  • 120
  • 174
Chiwai Chan
  • 4,716
  • 4
  • 30
  • 33
  • 2
    This doesn't address the issue of this callback being called when the spinner is first initiated (thus provoking a response which has nothing to do with an item actually being selected). – Yevgeny Simkin Feb 28 '11 at 18:45
8

The docs for the spinner-widget says

A spinner does not support item click events.

You should use setOnItemSelectedListener to handle your problem.

Ronin
  • 7,322
  • 6
  • 36
  • 54
johndotnet
  • 123
  • 1
  • 4
8

For kotlin you can use:

spinner.onItemSelectedListener =  object : AdapterView.OnItemSelectedListener {
    override fun onItemSelected(parent: AdapterView<*>?, view: View?, position: Int, id: Long) {
        
    }

    override fun onNothingSelected(p0: AdapterView<*>?) {
        
    }
}

Note: for parameters of onItemSelected method I use custom variable names

Abduhafiz
  • 3,318
  • 5
  • 38
  • 48
4

take a global variable for current selection of spinner:

int currentItem = 0;

spinner_counter = (Spinner)findViewById(R.id.spinner_counter);
String[] value={"20","40","60","80","100","All"};
aa=new ArrayAdapter<String>(this,R.layout.spinner_item_profile,value);
aa.setDropDownViewResource(android.R.layout.simple_spinner_dropdown_item);
spinner_counter.setAdapter(aa);

spinner_counter.setOnItemSelectedListener(new AdapterView.OnItemSelectedListener() {
        @Override
        public void onItemSelected(AdapterView<?> parent, View view, int position, long id) {
            if(currentItem == position){
                return; //do nothing
            }
            else
            {
                 TextView spinner_item_text = (TextView) view;
                 //write your code here
            }
            currentItem = position;
        }

        @Override
        public void onNothingSelected(AdapterView<?> parent) {

        }
    });

//R.layout.spinner_item_profile
<?xml version="1.0" encoding="utf-8"?>

<TextView  android:id="@+id/spinner_item_text"
xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent" 
android:layout_height="wrap_content"
android:background="@drawable/border_close_profile"
android:gravity="start"  
android:textColor="@color/black"         
android:paddingLeft="5dip"
android:paddingStart="5dip"
android:paddingTop="12dip"
android:paddingBottom="12dip"
/>

//drawable/border_close_profile
<?xml version="1.0" encoding="utf-8"?>
<layer-list xmlns:android="http://schemas.android.com/apk/res/android">
  <item>
   <shape android:shape="rectangle">
    <solid android:color="#e2e3d7" />
   </shape>
 </item>
<item android:left="1dp"
android:right="1dp"
android:top="1dp"
android:bottom="1dp">
<shape android:shape="rectangle">
    <solid android:color="@color/white_text" />
</shape>
</item>
</layer-list>
indrajeet
  • 341
  • 5
  • 9
4

If you want a true onChangedListener(). Store the initial value in the handler and check to see if it has changed. It is simple and does not require a global variable. Works if you have more than one spinner on the page.

String initialValue = // get from Database or your object
mySpinner.setOnItemSelectedListener(new SpinnerSelectedListener(initialValue));

...

protected class SpinnerSelectedListener implements AdapterView.OnItemSelectedListener {

        private SpinnerSelectedListener() {
            super();
        }

        public SpinnerSelectedListener(String initialValue) {
            this();
            this.initialValue = initialValue;
        }

        private String initialValue;

        // getter and setter removed.  

        @Override
        public void onItemSelected(AdapterView<?> parent, View view, int position, long id) {
            final String newValue = (String) spinHeight.getItemAtPosition(position);
            if (newValue.equals(initialValue) == false) {
               // Add your code here.  The spinner has changed value. 

               // Maybe useful.   
               // initialValue = newValue;
            }

        }

        @Override
        public void onNothingSelected(AdapterView<?> parent) {
               // Maybe useful.   
               // initialValue = null; 
        }
    }

Objects are your friend, use them.

fishjd
  • 1,617
  • 1
  • 18
  • 31
3
spinner.setOnItemSelectedListener(
            new AdapterView.OnItemSelectedListener() {

                @Override
                public void onItemSelected(AdapterView<?> arg0, View arg1,
                        int arg2, long arg3) {

                    // TODO Auto-generated method stub
                }

                @Override
                public void onNothingSelected(AdapterView<?> arg0) {
                    // TODO Auto-generated method stub

                }
                //add some code here
            }
        );
r-magalhaes
  • 427
  • 2
  • 9
  • 18
2

This will work intialize the spinner and findviewbyid and use this it will work

    Spinner schemeStatusSpinner;

  schemeStatusSpinner = (Spinner) dialog.findViewById(R.id.spinner);

schemeStatusSpinner.setOnItemSelectedListener(new AdapterView.OnItemSelectedListener() {
        @Override
        public void onItemSelected(AdapterView<?> parentView, View selectedItemView, int position, long id) {
            // your code here
            if(schemeStatusSpinner.getSelectedItemId()==4){
                reasonll.setVisibility(View.VISIBLE);
            }
            else {
                reasonll.setVisibility(View.GONE);
            }
        }

        @Override
        public void onNothingSelected(AdapterView<?> parentView) {
            // your code here
        }

    });
sanjay
  • 695
  • 11
  • 22
2

By default, you will get the first item of the spinner array through

value = spinner.getSelectedItem().toString();

whenever you selected the value in the spinner this will give you the selected value

if you want the position of the selected item then do it like that

pos = spinner.getSelectedItemPosition();

the above two answers are for without applying listener

Simas Joneliunas
  • 2,890
  • 20
  • 28
  • 35
Adam Noor
  • 101
  • 3
1

I know this was long solved but I have a "Please select" string at the top of my string arrays. Then when you write the listener

yourspinner.onItemSelectedListener = object : OnItemSelectedListener {
            override fun onItemSelected(adapterView: AdapterView<*>?, view: View, i: Int, l: Long) {
                yourvalue = yourspinner.getSelectedItem().toString()

                when(yourvalue){
                    "Please Select" -> // DO nothing
                    else -> // Do something
                }
            }

            override fun onNothingSelected(adapterView: AdapterView<*>?) {
                return
            }
        }

You can of course extend the when statement to have different responses or actions.

UnknownError
  • 103
  • 14
1

The best way what I think would be to have an flagitemselected = 0; in onCreate(). And on item selected event increment that flag i.e flagitemselected++; and then check

if(flagitemselected!=1)
{
// do your work here
}

This will help I guess.

ppreetikaa
  • 1,149
  • 2
  • 15
  • 22
Pinakin Kansara
  • 2,273
  • 3
  • 22
  • 35
0

One trick I found was putting your setOnItemSelectedListeners in onWindowFocusChanged instead of onCreate. I haven't found any bad side-effects to doing it this way, yet. Basically, set up the listeners after the window gets drawn. I'm not sure how often onWindowFocusChanged runs, but it's easy enough to create yourself a lock variable if you are finding it running too often.

I think Android might be using a message-based processing system, and if you put it all in onCreate, you may run into situations where the spinner gets populated after it gets drawn. So, your listener will fire off after you set the item location. This is an educated guess, of course, but feel free to correct me on this.

Joe Plante
  • 6,308
  • 2
  • 30
  • 23