Spinner onItemSelected called erroneously (without user action)

34,334

Solution 1

Androider, I have found a solution for this problem and posted it here (with code sample):

Spinner onItemSelected() executes when it is not suppose to

Solution 2

Another option in the spirit of Bill Mote's solution is to make the OnItemSelectedListener also an OnTouchListener. The user interaction flag can then be set to true in the onTouch method and reset in onItemSelected() once the selection change has been handled. I prefer this solution because the user interaction flag is handled exclusively for the spinner, and not for other views in the activity that may affect the desired behavior.

In code:

Create your listener for the spinner:

public class SpinnerInteractionListener implements AdapterView.OnItemSelectedListener, View.OnTouchListener {

    boolean userSelect = false;

    @Override
    public boolean onTouch(View v, MotionEvent event) {
        userSelect = true;
        return false;
    }

    @Override
    public void onItemSelected(AdapterView<?> parent, View view, int pos, long id) {
        if (userSelect) { 
            // Your selection handling code here
            userSelect = false;
        }
    }

}

Add the listener to the spinner as both an OnItemSelectedListener and an OnTouchListener:

SpinnerInteractionListener listener = new SpinnerInteractionListener();
mSpinnerView.setOnTouchListener(listener);
mSpinnerView.setOnItemSelectedListener(listener);

Solution 3

You can simply add an int count to solve it :)

 sp.setOnItemSelectedListener(new OnItemSelectedListener() {
    int count=0;
    @Override
    public void onItemSelected(AdapterView<?> arg0, View arg1, int arg2, long arg3) {
        if(count >= 1){
            int item = sp.getSelectedItemPosition();
            Toast.makeText(getBaseContext(),
                 "You have selected the book: " + androidBooks[item],
                  Toast.LENGTH_SHORT).show();
        }
        count++;
    }


    @Override
    public void onNothingSelected(AdapterView<?> arg0) {
    }
});

Solution 4

Beginning with API level 3 you can use onUserInteraction() on an Activity with a boolean to determine if the user is interacting with the device.

http://developer.android.com/reference/android/app/Activity.html#onUserInteraction()

@Override
public void onUserInteraction() {
    super.onUserInteraction();
    userIsInteracting = true;
}

As a field on the Activity I have:

private boolean userIsInteracting;

Finally, my spinner:

    mSpinnerView.setOnItemSelectedListener(new OnItemSelectedListener() {

        @Override
        public void onItemSelected(AdapterView<?> arg0, View view, int position, long arg3) {
            spinnerAdapter.setmPreviousSelectedIndex(position);
            if (userIsInteracting) {
                updateGUI();
            }
        }

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

        }
    });

As you come and go through the activity the boolean is reset to false. Works like a charm.

Solution 5

I have solved this problem a different way as I noticed sometimes the onItemSelected method was called twice on orientation change or sometimes once.

It seemed to depend on the current spinner selection. So a flag or counter didn't work for me. Instead I record the system time in BOTH onResume() and onCreate using widgetsRestartTime = System.currentTimeMillis()

widgetsRestartTime is declared as a double and as an intance variable.

Now in the onItemSelected() method I check the current time again and subtract widgetsRestartTime from it as follows: if (System.currentTimeMillis() - widgetsRestartTime > 200) { // user triggered event - so this is a real spinner event so process it } else { // system generated event e.g. orientation change, activity startup. so ignore }

Share:
34,334

Related videos on Youtube

Androider
Author by

Androider

Updated on July 09, 2022

Comments

  • Androider
    Androider almost 2 years

    I have a spinner which I am showing in a dialog view, and the moment the dialog starts onItemSelected is called. I don't really want to process this but only when user makes the selection. So I either need to prevent this (maybe because no default value is set?), or I need to know it is not the user that is making this selection?

    • ninjasense
      ninjasense over 13 years
      Some code would be helpful. If you are setting a selection programatically this will cause the onItemSelected to get called
    • Jonik
      Jonik almost 10 years
      Duplicate of stackoverflow.com/questions/2562248/… which was asked earlier and contains better answers (specifically this one).
  • Androider
    Androider over 13 years
    I suppose the indicator would be a flag of some sort inside the onclick.
  • Giorgio Vespucci
    Giorgio Vespucci about 13 years
    @Androider Could you tell me please how did you solved this? I tried with a flag in onCreate(). It works when the application is first created but I have problem after orientation change. Thank you
  • Androider
    Androider about 13 years
    new View.OnClickListener() { Boolean autoSelected=true; // declare here public void onClick(View v) {
  • Androider
    Androider about 13 years
    someSpinner.setOnItemSelectedListener(new OnItemSelectedListener() { public void onItemSelected(AdapterView<?> parent, View view, int index, long arg3) {
  • Androider
    Androider about 13 years
    if (autoSelected==false){ autoSelected=true; viewDialog.cancel(); }else { autoSelected=false; }
  • Androider
    Androider about 13 years
    where someSpinner is pulled out of ViewOnClickListner from dialog via
  • Androider
    Androider about 13 years
    Spinner someSpinner = (Spinner) dialogView. findViewById(R.id.viewSpin);
  • Heiko Rupp
    Heiko Rupp about 12 years
    It looks like it may fail on HC and ICS. Has anyone seen that before?
  • Qwertie
    Qwertie about 12 years
    Hahaha, a link to the question that was closed as a duplicate of this question ... is accepted as the answer to this question :)
  • rds
    rds almost 12 years
    I don't understand why you compare the content of the String. I think it is more elegant to set a constant TAG_INITIALIZING="";(or any other object) and then to compare with `` if (parentView.getTag() == TAG_INITIALIZING) {parentView.setTag(null);return;}``
  • XMight
    XMight almost 12 years
    Ye, but you forget that next time you must check against null, and if anywhere else you don't do this check, you will get NullReferenceException. It's more safe to use like that. Also, like this, you can reuse the tag for any other string value, for example I need such a thing in my app :) (i.e. I check against multiple values of the tag)
  • Ajith M A
    Ajith M A about 11 years
    I too did the same as i had to remember the postiion for some other reasons. Thanks buddy for the hint
  • Henrique de Sousa
    Henrique de Sousa over 10 years
    It does fail on ICS, seems it only works on Gingerbread_MR1 and below.
  • Darwind
    Darwind over 10 years
    This is definitely the best solution - will work for multiple spinners as well - thanks :-)
  • ygesher
    ygesher almost 10 years
    I think you're assuming that the item at position 0 is the prompt. Even so, what if the user changes his mind, and decides to select a second time. Then his choice will be bumped down one? Not desirable behavior. I liked the concept though, and implemented with a boolean field instead.
  • ThomasRS
    ThomasRS almost 10 years
    I believe the NothingSelectedSpinnerAdapter never changes the number of items, however uses isEnabled(int position) false for index 0.
  • Mike Richards
    Mike Richards over 9 years
    In my case, I just used a boolean isLoaded as that made more sense than a counter
  • Sufian
    Sufian over 9 years
    The bestest solution ever! Saves me from those unwanted item selects while restoring Fragment's state.
  • Amandeep Rohila
    Amandeep Rohila almost 9 years
    Thanks a lot.. after doing lots of stuff nothing works in my code because adapter data notify at runtime but solution given by you userIsInteracting resolved the issue
  • Muhammed Refaat
    Muhammed Refaat over 8 years
    the pure clarification of the word "Plot Twist"
  • saiyancoder
    saiyancoder almost 8 years
    Actually the word would be "recursion"
  • tbm
    tbm almost 8 years
    Can you elaborate on "as you come and go through the activity" - because nothing in your code sets userIsInteracting to false.
  • Bill Mote
    Bill Mote almost 8 years
    You are correct. The interaction that is causing the problem has to do with the spinner being initially populated. It erroneously calls onItemSelected() as items are added to the list. If the list is already populated then you're probably not adding anything again and/or you've left the activity and you're coming back through onCreate() which would reset the value.
  • Pierre
    Pierre over 7 years
    Simple and easy solution !
  • Srujan Simha
    Srujan Simha over 7 years
    Infinite loop. .
  • LarsH
    LarsH almost 5 years
    @Mike Yes, unless you have multiple spinners using the same listener.
  • LarsH
    LarsH almost 5 years
    What do you mean by "what you are seeing is correct"? Do you mean the OP has correctly observed the actual behavior, or do you mean the behavior is correct (according to the documentation)? The documentation says that setOnItemSelectedListener() will "Register a callback to be invoked when an item in this AdapterView has been selected." One could argue that an item in the adapterview gets selected internally during initialization, but that's not clear, intuitive, nor guaranteed from what the doc says.
  • user924941
    user924941 about 3 years
    Absolutely the best solution, much better then adding booleans to ignore the 1st selection