I'm trying to customize a Spinner to be multi-selectable. I have successfully made it multi-selectable, but when an item is reselected, the view inside of the spinner does not get updated.
When an item is reselected in the Spinner
, the getView()
of my SpinnerAdapter
gets called, produces the View
that I want it to, but that View
somehow does not get displayed. I have stepped through everything in the debugger and I cannot find any differences between when a different item is selected or the same item is reselected. I have been through all of the code in Spinner
AbsSpinner
and AdapterView
and I cannot find what may be causing this.
The spinner is populated with 6 items: Choice 1-6. The spinner originates with all items unselected. Choice 1, 2, and 4 are selected. The view inside the spinner displays "Choice 1, Choice 2, Choice 4" correctly.
Choice 4 is unselected (reselection as far as the Spinner is concerned). Choice 4 is correctly unchecked from the list, but the view inside the spinner does not update. It still displays "Choice 1, Choice 2, Choice 4". Stepping through the debugger, getView() on my SpinnerAdapter gets called and everything. For some reason, the View doesn't actually get displayed.
Choice 2 is unselected (this is not a "re-selection" to the Spinner). Here everything functions as expected. Both the list items and the view inside the Spinner are updated.
public class MultiSelectSpinner extends Spinner {
private OnItemSelectedListener listener;
public MultiSelectSpinner(Context context) {
super( context );
}
public MultiSelectSpinner(Context context, AttributeSet attrs) {
super( context, attrs );
}
public MultiSelectSpinner(Context context, AttributeSet attrs, int defStyle) {
super( context, attrs, defStyle );
}
@Override
public void setSelection( int position ) {
super.setSelection( position );
if ( listener != null ) {
listener.onItemSelected( this, getSelectedView(), position, getAdapter().getItemId( position ) );
}
}
public void setOnItemSelectedEvenIfUnchangedListener( OnItemSelectedListener listener ) {
this.listener = listener;
}
}
Only posting the important pieces here. Leaving out getters/setters, etc.
public class FormChoiceSpinnerAdapter implements SpinnerAdapter, OnItemSelectedListener {
private Choice[] choices;
private String title;
private final DataSetObservable dataSetObservable = new DataSetObservable();
public FormChoiceSpinnerAdapter(String[] choices, String title) {
setChoices( new Choice[choices.length] );
for (int i = 0; i < choices.length; i++) {
getChoices()[i] = new Choice( choices[i] );
}
}
@Override
public View getView( int position, View convertView, ViewGroup parent ) {
Context context = parent.getContext();
if ( convertView == null ) {
convertView = LayoutInflater.from( context ).inflate( android.R.layout.simple_spinner_item, parent, false );
}
String displayString = "";
for (int i = 0; i < getChoices().length; i++) {
Choice choice = getChoices()[i];
if ( choice.isSelected() ) {
displayString += choice.getLabel() + ", ";
}
}
if ( displayString.length() > 0 ) {
displayString = displayString.trim().substring( 0, displayString.length() - 2 );
}
else {
displayString = getTitle() + "...";
}
( (TextView) convertView ).setText( displayString );
return convertView;
}
@Override
public View getDropDownView( int position, View convertView, ViewGroup parent ) {
Context context = parent.getContext();
if ( convertView == null ) {
convertView = LayoutInflater.from( context ).inflate( R.layout.simple_dropdown_item, parent, false );
}
Choice choice = getChoices()[position];
TextView text = (TextView) convertView.findViewById( android.R.id.text1 );
text.setText( choice.getLabel() );
ImageView selectedImage = (ImageView) convertView.findViewById( R.id.image_selected );
int visibility = choice.isSelected() ? View.VISIBLE : View.GONE;
selectedImage.setVisibility( visibility );
return convertView;
}
@Override
public void onItemSelected( AdapterView<?> parent, View view, int position, long id ) {
Choice choice = getChoices()[position];
choice.setSelected( !choice.isSelected() );
ImageView imageView = (ImageView) view.findViewById( R.id.image_selected );
if ( imageView != null ) {
imageView.setVisibility( choice.isSelected() ? View.VISIBLE : View.GONE );
}
}
@Override
public void onNothingSelected( AdapterView<?> parent ) {
//No op
}
public static class Choice {
private boolean selected;
private String label;
public Choice(String label) {
this.label = label;
selected = false;
}
}
}
I know this is very late, but I found a solution for a similar problem I was having. Try adding adapter.notifyDataSetChanged()
to your onItemSelected(AdapterView<?> parent, View view, int pos, long id)
Override method.
My spinner is supposed to show a default message until the user actually selects a value from it. It was working fine unless to user selected the first item in the dropdown, then the spinner wouldn't update itself. Adding adapter.notifyDataSetChanged()
worked.
I also looked through some of the source code and I couldn't find exactly what was happening, but I have an idea. Since the Spinner
doesn't call the OnItemSelectedListener
when selecting the value that is already selected (unless you extend Spinner
, which I did with the help of this SO answer), my guess is that if you try to deselect your most recently selected value, Spinner
doesn't recognize that as a deselection, and therefore won't redraw the view. For you, selecting 1, 2, 4 in that order, then trying to deselect 4 wouldn't trigger the redraw response, but then deselecting 2 would cause the redraw.
I could be wrong, but I know that our problems were similar and this worked for me.
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