The OnItemSelectedListener event handler gets called both when a spinner selection is changed programmatically, and when a user physically clicks the spinner control. Is is possible to determine if an event was triggered by a user selection somehow?
Or is there another way to handle spinner user selections?
Use ArrayAdapter to store the courses list. Create a single MainActivity that contains the spinner and on clicking any item of spinner Toast with that course name will be shown. Creating the activities: There will be one activity and hence one XML file for MainActivity. activity_main.
In Android, Spinner is used to select one value from a set of values. In the default state, a spinner shows its currently selected value. Touching the spinner displays a drop down menu with all other available values, from which the user can select a new one. Android spinner is associated with AdapterView .
To workaround you need to remember the last selected position. Then inside of your spinner listener compare the last selected position with the new one. If they are different, then process the event and also update the last selected position with new position value, else just skip the event processing.
If somewhere within the code you are going to programatically change spinner selected position and you don't want the listener to process the event, then just reset the last selected position to the one you're going to set.
Yes, Spinner in Android is painful. I'd even say pain starts from its name - "Spinner". Isn't it a bit misleading? :) As far as we're talking about it you should also be aware there's a bug - Spinner may not restore (not always) its state (on device rotation), so make sure you handle Spinner's state manually.
Hard to believe that a year and a half later, the problem still exists and continues to boggle people...
Thought I'd share the workaround I came up with after reading Arhimed's most useful post (thanks, and I agree about spinners being painful!). What I've been doing to avoid these false positives is to use a simple wrapper class:
import android.util.Log; import android.view.View; import android.widget.AdapterView; import android.widget.AdapterView.OnItemSelectedListener; public class OnItemSelectedListenerWrapper implements OnItemSelectedListener { private int lastPosition; private OnItemSelectedListener listener; public OnItemSelectedListenerWrapper(OnItemSelectedListener aListener) { lastPosition = 0; listener = aListener; } @Override public void onItemSelected(AdapterView<?> aParentView, View aView, int aPosition, long anId) { if (lastPosition == aPosition) { Log.d(getClass().getName(), "Ignoring onItemSelected for same position: " + aPosition); } else { Log.d(getClass().getName(), "Passing on onItemSelected for different position: " + aPosition); listener.onItemSelected(aParentView, aView, aPosition, anId); } lastPosition = aPosition; } @Override public void onNothingSelected(AdapterView<?> aParentView) { listener.onNothingSelected(aParentView); } }
All it does is trap item selected events for the same position that was already selected (e.g. the initial automatically triggered selection for position 0), and pass on other events to the wrapped listener. To use it, all you have to do is modify the line in your code that calls the listener to include the wrapper (and add the closing bracket of course), so instead of, say:
mySpinner.setOnItemSelectedListener(new OnItemSelectedListener() { ... });
you'd have this:
mySpinner.setOnItemSelectedListener(new OnItemSelectedListenerWrapper(new OnItemSelectedListener() { ... }));
Obviously once you've tested it, you could get rid of the Log calls, and you could add the ability to reset the last position if required (you'd have to keep a reference to the instance, of course, rather than declaring on-the-fly) as Arhimed said.
Hope this can help someone from being driven crazy by this strange behaviour ;-)
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With