Simplified Android Spinner I have written a simple spinner wrapper, but was wondering if any of you could think of any ways to make it more robust. It only handles strings at the moment.

MySpinner:

package a.b.c;

import android.content.Context;
import android.util.AttributeSet;
import android.widget.ArrayAdapter;
import android.widget.Spinner;

public class MySpinner extends Spinner {

    // constructors (each calls initialise)
    public MySpinner(Context context) {
        super(context);
        this.initialise();
    }
    public MySpinner(Context context, AttributeSet attrs) {
        super(context, attrs);
        this.initialise();
    }
    public MySpinner(Context context, AttributeSet attrs, int defStyle) {
        super(context, attrs, defStyle);
        this.initialise();
    }

    // declare object to hold data values
    private ArrayAdapter&lt;String&gt; arrayAdapter;

    // add the selected item to the end of the list
    public void addItem(String item) {
        this.addItem(item, true);
    }
    public void addItem(String item, boolean select) {
        arrayAdapter.add(item);
        this.setEnabled(true);
        if (select) this.selectItem(item);
        arrayAdapter.sort(new Comparator&lt;String&gt;() {
            public int compare(String object1, String object2) {
                return object1.compareTo(object2);
            };
        });
    }

    // remove all items from the list and disable it
    public void clearItems() {
        arrayAdapter.clear();
        this.setEnabled(false);
    }

    // make the specified item selected (returns false if item not in the list)
    public boolean selectItem(String item) {
        boolean found = false;
        for (int i = 0; i &lt; this.getCount(); i++) {
            if (arrayAdapter.getItem(i) == item) {
                this.setSelection(i);
                found = true;
                break;
            }
        }
        return found;
    }

    // return the current selected item
    public String getSelected() {
        if (this.getCount() &gt; 0) {
            return arrayAdapter.getItem(super.getSelectedItemPosition());
        } else {
            return "";
        }
    }

    // allow the caller to use a different DropDownView, defaults to android.R.layout.simple_dropdown_item_1line
    public void setDropDownViewResource(int resource) {
        arrayAdapter.setDropDownViewResource(resource);
    }
    // internal routine to set up the array adapter, bind it to the spinner and disable it as it is empty
    private void initialise() {
        arrayAdapter = new ArrayAdapter&lt;String&gt;(super.getContext(), android.R.layout.simple_spinner_item);
        arrayAdapter.setDropDownViewResource(android.R.layout.simple_dropdown_item_1line);
        this.setAdapter(arrayAdapter);
        this.setEnabled(false);
    }
}


To use:


Use a.b.c.MySpinner instead of Spinner in your XML layout file
Set up a variable, mMySpinner = (MySpinner)findViewById(R.id.spinner);
You can then use all the functions which should be self-explanatory
If there are no items in the list, the spinner is disabled to prevent untoward events





mMySpinner.clearItems() - to remove all the items
mMySpinner.addItem("Blue") - to add Blue as an item in list (items are sorted by abc)
mMySpinner.selectItem("Red") - to make the indicate item the current selection
mMySpinner.getSelected() - to return the current selected item string
This is a very nice class, and I don't wish to discredit the author (FrinkTheBrave) in any way!  I do, however, want to offer a few enhancements and correct the nasty bugs that are inherent in the original.

The issues/enhancements addressed here:


  o Do not insert duplicates
  
  o Re-do selection if new row inserted
  
  o getSelected will return null rather than empty string if nothing selected
  
  o Proper comparison of string (a.equals(b) rather than a == b)
  
  o Sort is now case-insensitive
  
  o Removed 'this.' from this.foo where unneeded (which means, everywhere).
  
  o Changed setDropDownViewResource as per comment by @seand


To explain more about the re-selection:  Consider the following.  Spinner currently has these items (where > indicates the selected item) 

    Antelope
  &gt; Cat
    Dog


Now, we insert Bear.  Unless we re-do it, the second item will remain as 'selected.'  But because we inserted Bear alphabetically, the true Selected item has moved to the third position.

    Antelope
  &gt; Bear
    Cat
    Dog


So, before the insertion of an non-selected String, we will save the String of the current selection (if there is one).  Then, after the insertion and the sort, we will select it again.

    Antelope
    Bear
  &gt; Cat
    Dog  


So if I may be so bold....

package c.b.a;

import java.util.Comparator;

import android.content.Context;
import android.util.AttributeSet;
import android.widget.ArrayAdapter;
import android.widget.Spinner;

public class AdvancedSpinner extends Spinner {

    // Thanks to CodeReview: http://codereview.stackexchange.com/questions/491/simplified-android-spinner
    // declare object to hold data values
    private ArrayAdapter&lt;String&gt; arrayAdapter;

    // constructors (each call initialize)
    public AdvancedSpinner(Context context) {
        super(context);
        initialize();
    }

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

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

    // add the selected item to the end of the list
    public Boolean addItem(String item) {
        return addItem(item, true);
    }

    public Boolean addItem(String item, boolean select) {
        Boolean addFlag = true;

        for (int i = 0; i &lt; getCount(); i++) {
            if (arrayAdapter.getItem(i).equals(item)) {
                // Don't add an item that's already in the list
                // But we do need to mark it as "selected" if
                // applicable.
                addFlag = false;
                if (select)
                    setSelection(i);
                break;
            }
        }
        if (addFlag) {
            String saveSelected = null;
            if (! select) {
                // We need to preserve the prior selection, which may be
                // messed up by our array sort operation.  So we will
                // save the selected item's value, and (after the insert)
                // we will reinstate its selection status.
                saveSelected = getSelected();
            }
            arrayAdapter.add(item);
            setEnabled(true);
            arrayAdapter.sort(new Comparator&lt;String&gt;() {
                public int compare(String object1, String object2) {
                    return  object1.compareToIgnoreCase(object2);
                };
            });
            if (select)
                selectItem(item);
            else if (saveSelected != null)
                selectItem(saveSelected);
        }
        return addFlag;
    }

    // remove all items from the list and disable it
    public void clearItems() {
        arrayAdapter.clear();
        setEnabled(false);
    }

    // make the specified item selected (returns false if item not in the list)
    public boolean selectItem(String item) {
        boolean found = false;
        for (int i = 0; i &lt; getCount(); i++) {
            if (arrayAdapter.getItem(i).equals(item)) {
                setSelection(i);
                found = true;
                break;
            }
        }
        return found;
    }

    public String getSelected() {
        // return the current selected item
        // Changed (Dennis) to return null if nothing selected, rather than the
        // (misleading) empty string.  Also, prior incarnation would ForceClose
        // if array was not empty but nothing was selected.
        String rtnVal = null;
        if (getCount() &gt; 0) {
            int i = super.getSelectedItemPosition();
            if (i &gt;= 0)  // An item has been selected
                rtnVal = arrayAdapter.getItem(i);
        }
        return rtnVal;
    }

    // allow the caller to use a different DropDownView, defaults to android.R.layout.simple_spinner_dropdown_item
    public void setDropDownViewResource(int resource) {
        arrayAdapter.setDropDownViewResource(resource);
    }

    // internal routine to set up the array adapter, bind it to the spinner and disable it as it is empty
    private void initialize() {
        arrayAdapter = new ArrayAdapter&lt;String&gt;(super.getContext(), android.R.layout.simple_spinner_item);
        arrayAdapter.setDropDownViewResource(android.R.layout.simple_spinner_dropdown_item);
        setAdapter(arrayAdapter);
        setEnabled(false);
    }
}