ChatGPT解决这个技术问题 Extra ChatGPT

Android Spinner: Get the selected item change event

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
}
I've tried these answers, but no one was helpful. Once Spinner component doesn't support item click events. Spinner Documentation

S
Shine

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.


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?
actually that problem can be solved by putting the setOnItemSelectedListener in the override OnStart method and not in the onCreate method. stupid me...
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?
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.
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.)
V
Vasily Kabunov
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:

Spinner initialization User selected manually

Try to differentiate those two by using flag variable.


Mine gets called twice too! I cant figure out how you differentiate between the two?
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).
Best explanation about execution of OnclickListener.
I am personally appalled that something so simple - such a fundamental UI widget - is so bloody difficult to implement... seriously - how hard would it be to build a 'default showing item' property and to build the Boolean flag property into the object class itself?? I'm no fan of Objective C but I will say that iOS widget implementation takes about 1/10th the time it does in Android.
I also agree. The Spinner is one screwed up widget. Its very hard to know when the popup or drop is open or closed. Would it have been so hard to add an event for this? The above solution can ALMOST tell you when the list is open or closed but has three problems: (1) there is no event for selecting the already selected item (and the list closes) and (2) there is no event for abort (touch off-list to close it) and (3) onNothingSelected never seems to fire for me.
d
dakshbhatt21

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("");
}

how do i get the "selection"?
C
Community

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!


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 was correct if you use this the int must be final. In any case it works really well. I was disabling an EditText field if position 0 was not selected and if it changed re-enable it. Thanks for this.
L
Leonid Ustenko

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;
            }
        });

Thank you for using this flag.
d
dakshbhatt21

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
    }
});

A
Abduhafiz

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


R
Ronin

The docs for the spinner-widget says

A spinner does not support item click events.

You should use setOnItemSelectedListener to handle your problem.


G
Guido
spinner1.setOnItemSelectedListener(
    new AdapterView.OnItemSelectedListener() {
        //add some code here
    }
);

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).
i
indrajeet

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>

f
fishjd

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.


r
r-magalhaes
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
            }
        );

s
sanjay

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
        }

    });

how we can write it with lambda?
S
Simas Joneliunas

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


p
ppreetikaa

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.


U
UnknownError

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.


J
Joe Plante

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.