Spinner : onItemSelected not called when selected item remains the same

AndroidSpinnerListenerAndroid ActionbarActionbarsherlock

Android Problem Overview


I have a OnItemSelectedListener for my Spinner, but it is not called when the selected item is the same as the previous one. Apparently the OnClickListener is not an option for a Spinner. I need to catch everytime a user click on an item. Any idea?

Maybe the fact that this Spinner is inside the ActionBar disturbs normal behavior?

@Override
public void onCreateOptionsMenu(Menu menu, MenuInflater inflater) {
	inflater.inflate(R.menu.tracklist_menu, menu);
	Spinner spinner = (Spinner) menu.findItem(R.id.option_ordering_spinner)
			.getActionView();
	spinner.setAdapter(mSpinnerAdapter);
	spinner.setSelection(PrefsHelper.getOrderingSpinnerPos(prefs));
	spinner.setOnItemSelectedListener(new OnItemSelectedListener() {

		@Override
		public void onItemSelected(AdapterView<?> parent, View view,
				int position, long id) {
			String str = "selected";
			System.out.println(str);
			if (optionMenuInitialized) {

				switch (position) {
				case 0:
					// rdm
					getActivity()
							.sendBroadcast(
									new Intent(
											MyIntentAction.DO_RESHUFFLE_PLAYLIST));
					smp.setCurrentTracklistCursorPos(-1);
					trackAdapter.notifyDataSetChanged();
					break;
				case 1:
					// artist
					getActivity()
							.sendBroadcast(
									new Intent(
											MyIntentAction.DO_ORDER_PLAYLIST_BY_ARTIST));
					smp.setCurrentTracklistCursorPos(-1);
					trackAdapter.notifyDataSetChanged();
					break;
				case 2:
					// folder
					getActivity()
							.sendBroadcast(
									new Intent(
											MyIntentAction.DO_ORDER_PLAYLIST_BY_FOLDER));
					smp.setCurrentTracklistCursorPos(-1);
					trackAdapter.notifyDataSetChanged();
					break;
				}
				PrefsHelper.setOrderingSpinnerPos(prefEditor, position);
				prefEditor.commit();
			}
			optionMenuInitialized = true;
		}

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

Android Solutions


Solution 1 - Android

Ok, I finally found a solution, by creating my own class extending Spinner :

public class MySpinner extends Spinner {
OnItemSelectedListener listener;

public MySpinner(Context context, AttributeSet attrs) {
	super(context, attrs);
}

@Override
public void setSelection(int position) {
	super.setSelection(position);
	if (listener != null)
		listener.onItemSelected(null, null, position, 0);
}

public void setOnItemSelectedEvenIfUnchangedListener(
		OnItemSelectedListener listener) {
	this.listener = listener;
}
}

Solution 2 - Android

I found out this work instead of the one provided

/** Spinner extension that calls onItemSelected even when the selection is the same as its previous value */
public class NDSpinner extends Spinner {

  public NDSpinner(Context context)
  { super(context); }

  public NDSpinner(Context context, AttributeSet attrs)
  { super(context, attrs); }

  public NDSpinner(Context context, AttributeSet attrs, int defStyle)
  { super(context, attrs, defStyle); }

  @Override public void
  setSelection(int position, boolean animate)
  {
    boolean sameSelected = position == getSelectedItemPosition();
    super.setSelection(position, animate);
    if (sameSelected) {
      // Spinner does not call the OnItemSelectedListener if the same item is selected, so do it manually now
      getOnItemSelectedListener().onItemSelected(this, getSelectedView(), position, getSelectedItemId());
    }
  }

  @Override public void
  setSelection(int position)
  {
    boolean sameSelected = position == getSelectedItemPosition();
    super.setSelection(position);
    if (sameSelected) {
      // Spinner does not call the OnItemSelectedListener if the same item is selected, so do it manually now
      getOnItemSelectedListener().onItemSelected(this, getSelectedView(), position, getSelectedItemId());
    }
  }
}

Solution 3 - Android

To make your spinner change despite the value of the last index selected just use a:

spinner.setSelection(0); 

before your other selection is called

spinner.setSelection(number); 

this way, the spinner will trigger two times the OnItemSelected event. Just make sure the second time it does whatever you need.

Solution 4 - Android

Rewrote the common solution but with:

  1. androidx in mind
  2. extended from AppCompatSpinner
  3. use built-in OnItemSelectedListener listener instead of creating own one
  4. added initial listener call hack

Here:

import android.content.Context;

import androidx.annotation.Nullable;
import androidx.appcompat.widget.AppCompatSpinner;


public class FixedSpinner extends AppCompatSpinner {
    // add other constructors that you need
    public FixedSpinner(Context context, int mode) {
        super(context, mode);
    }

    private void processSelection(int position) {
        boolean sameSelected = position == getSelectedItemPosition();
        final OnItemSelectedListener listener = getOnItemSelectedListener();
        if (sameSelected && listener != null) {
            // Spinner does not call the OnItemSelectedListener if the same item is selected, so do it manually now
            listener.onItemSelected(this, getSelectedView(), position, getSelectedItemId());
        }
    }

    @Override
    public void setSelection(int position) {
        processSelection(position);
        super.setSelection(position);
    }

    @Override
    public void setSelection(int position, boolean animate) {
        processSelection(position);
        super.setSelection(position, animate);
    }

    @Override
    public void setOnItemSelectedListener(@Nullable OnItemSelectedListener listener) {
        // This hack fixes bug, when immediately after initialization, listener is called
        // To make this fix work, first add data, only then set listener
        // Having done this, you may refresh adapter data as many times as you want
        setSelection(0, false);
        super.setOnItemSelectedListener(listener);
    }
}

Solution 5 - Android

Here a little better implementation:

public class SpinnerPlus extends Spinner {
    AdapterView.OnItemSelectedListener listener;

    public SpinnerPlus(Context context, AttributeSet attrs) {
        super(context, attrs);
    }

    @Override
    public void setSelection(int position) {
        super.setSelection(position);
        if (listener != null)
            listener.onItemSelected(this, getSelectedView(), position, 0);
    }

    public void setOnItemSelectedEvenIfUnchangedListener(
            AdapterView.OnItemSelectedListener listener) {
        this.listener = listener;
    }
}

Solution 6 - Android

Here is a better implementation -

Custom Spinner Class -

import android.content.Context;
import android.util.AttributeSet;
import androidx.appcompat.widget.AppCompatSpinner;

public class CSpinner extends AppCompatSpinner {

    private int lastPosition = 0;

    public CSpinner(Context context, AttributeSet attrs) {
        super(context, attrs);
    }

    @Override
    public void setSelection(int position) {
        super.setSelection(position);
        boolean sameSelected = lastPosition == getSelectedItemPosition();
        OnItemSelectedListener onItemSelectedListener = getOnItemSelectedListener();
        if (sameSelected && onItemSelectedListener != null) {
            onItemSelectedListener.onItemSelected(this, getSelectedView(), position, getSelectedItemId());
        }
        lastPosition = position;
    }
}

Setting Listener -

spn.setOnItemSelectedListener(new AdapterView.OnItemSelectedListener() {
        @Override
        public void onItemSelected(AdapterView<?> parent, View view, int position, long id) {
            Log.d("onItemSelected", String.valueOf(position));
        }

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

Solution 7 - Android

If it's still actual, correct call of the callback should be

@Override
public void setSelection(int position) {
    super.setSelection(position);
    if(listener != null)
        listener.onItemSelected(this, getChildAt(position), position, 0);
}

Solution 8 - Android

Simplest solution :

spinner.performItemClick(view,position,id)

Solution 9 - Android

I had same problem, I solved it by setting onItemSelectedListener every time adapter changes items.

Solution 10 - Android

Further from the answer from @Vishal Yadav, if you want to set an initial position without triggering the OnItemSelectedListener by calling spinner.setSelection(pos, false); then the custom spinner should be:

public class CSpinner extends AppCompatSpinner {

    private int lastPosition = 0;

    public CSpinner(Context context, AttributeSet attrs) {
        super(context, attrs);
    }

    @Override
    public void setSelection(int position, boolean animate) {
        OnItemSelectedListener listener = getOnItemSelectedListener();
        setOnItemSelectedListener(null);
        super.setSelection(position, animate);
        lastPosition = position;
        setOnItemSelectedListener(listener);
    }

    @Override
    public void setSelection(int position) {
        super.setSelection(position);
        boolean sameSelected = lastPosition == getSelectedItemPosition();
        OnItemSelectedListener onItemSelectedListener = getOnItemSelectedListener();
        if (sameSelected && onItemSelectedListener != null) {
            onItemSelectedListener.onItemSelected(this, getSelectedView(), position, getSelectedItemId());
        }
        lastPosition = position;
    }
}

Solution 11 - Android

i found a simple solution

just call setAdapter again in place of notifyDataSetChanged for the second spinner

Attributions

All content for this solution is sourced from the original question on Stackoverflow.

The content on this page is licensed under the Attribution-ShareAlike 4.0 International (CC BY-SA 4.0) license.

Content TypeOriginal AuthorOriginal Content on Stackoverflow
QuestionelguiView Question on Stackoverflow
Solution 1 - AndroidelguiView Answer on Stackoverflow
Solution 2 - AndroidEric Leong Zhia ChoongView Answer on Stackoverflow
Solution 3 - AndroidRamiro G.M.View Answer on Stackoverflow
Solution 4 - AndroidsoshialView Answer on Stackoverflow
Solution 5 - AndroidAlecsView Answer on Stackoverflow
Solution 6 - AndroidVishal YadavView Answer on Stackoverflow
Solution 7 - AndroidMartin EdlmanView Answer on Stackoverflow
Solution 8 - AndroidNishant ShahView Answer on Stackoverflow
Solution 9 - AndroidMarek KondrackiView Answer on Stackoverflow
Solution 10 - AndroidLeoXJView Answer on Stackoverflow
Solution 11 - AndroidAbhishek VishwakarmaView Answer on Stackoverflow