How to set Spinner default value to null?
Asked Answered
W

11

80

I'm trying to get a Spinner to load up with no selected value. Once the user selects a value it then takes them to another page.

This is proving to be a problem because at present, the page just loads straight away before the user gets a choice to choose.

My spinner class is set up the same way as Google's: http://developer.android.com/resources/tutorials/views/hello-spinner.html

So basically, is it possible have a spinner that loads with nothing selected because at present, it loads the first item in my string array.

Waw answered 18/1, 2011 at 16:34 Comment(4)
Check for #868018 Especially answer 40. Work like magic!Pyxidium
I posted a really nice solution to this here: https://mcmap.net/q/260789/-how-to-set-my-first-value-default-value-in-spinner-as-emptyOrdonez
Check out aaronvargas answer here: #868018Grabowski
I also posted a really nice solution to this here, Please Check :https://mcmap.net/q/260789/-how-to-set-my-first-value-default-value-in-spinner-as-emptyPsychotomimetic
R
96

is it possible have a spinner that loads with nothing selected

Only if there is no data. If you have 1+ items in the SpinnerAdapter, the Spinner will always have a selection.

Spinners are not designed to be command widgets. Users will not expect a selection in a Spinner to start an activity. Please consider using something else, like a ListView or GridView, instead of a Spinner.


EDIT

BTW, I forgot to mention -- you can always put an extra entry in your adapter that represents "no selection", and make it the initial selected item in the Spinner.

Roentgenoscope answered 18/1, 2011 at 16:47 Comment(4)
Ok thanks.Ive just gone for a simple button at present with an alertbox to choose which screen they go to. Its only a prototype atm. thanks for your helpWaw
What about displaying the items? I don't want the "dummy one" to be selected.Plummy
ListPopupWindow is better option in this case. By replacing spinner from textView and calling ListPopupWindow.show() on textView click shows the same behaviour with no drawback of default item selection. For more detail see this developer.android.com/reference/android/widget/…Expand
This answer doesn't help as others do. It shouldn't be accepted.Selig
C
31

Alternatively, you could override your spinner adapter, and provide an empty view for position 0 in your getView method, and a view with 0dp height in the getDropDownView method.

This way, you have an initial text such as "Select an Option..." that shows up when the spinner is first loaded, but it is not an option for the user to choose (technically it is, but because the height is 0, they can't see it).

Councillor answered 14/2, 2012 at 23:22 Comment(2)
Has anyone actually tried this? Seems like it is not as straightforward as it sounds.Doss
I posted one full implementation of this idea in a new answer. You need to take a couple of things into account, such as overriding getCount(), but it's doable.Piccadilly
P
31

This is a complete implementation of Paul Bourdeaux's idea, namely returning a special initial view (or an empty view) in getView() for position 0.

It works for me and is relatively straightforward. You might consider this approach especially if you already have a custom adapter for your Spinner. (In my case, I was using custom adapter in order to easily customise the layout of the items, each item having a couple of TextViews.)

The adapter would be something along these lines:

public class MySpinnerAdapter extends ArrayAdapter<MyModel> {

    public MySpinnerAdapter(Context context, List<MyModel> items) {
        super(context, R.layout.my_spinner_row, items);
    }

    @Override
    public View getDropDownView(int position, View convertView, @NonNull ViewGroup parent) {
        if (position == 0) {
            return initialSelection(true);
        }
        return getCustomView(position, convertView, parent);
    }

    @NonNull
    @Override
    public View getView(int position, View convertView, @NonNull ViewGroup parent) {
        if (position == 0) {
            return initialSelection(false);
        }
        return getCustomView(position, convertView, parent);
    }


    @Override
    public int getCount() {
        return super.getCount() + 1; // Adjust for initial selection item
    }

    private View initialSelection(boolean dropdown) {
        // Just an example using a simple TextView. Create whatever default view 
        // to suit your needs, inflating a separate layout if it's cleaner.
        TextView view = new TextView(getContext());
        view.setText(R.string.select_one);
        int spacing = getContext().getResources().getDimensionPixelSize(R.dimen.spacing_smaller);
        view.setPadding(0, spacing, 0, spacing);

        if (dropdown) { // Hidden when the dropdown is opened
            view.setHeight(0);
        }

        return view;
    }

    private View getCustomView(int position, View convertView, ViewGroup parent) {
        // Distinguish "real" spinner items (that can be reused) from initial selection item
        View row = convertView != null && !(convertView instanceof TextView) 
        ? convertView :
        LayoutInflater.from(getContext()).inflate(R.layout.my_spinner_row, parent, false);

        position = position - 1; // Adjust for initial selection item
        MyModel item = getItem(position);

        // ... Resolve views & populate with data ...

        return row;
    }

}

That's it. Note that if you use a OnItemSelectedListener with your Spinner, in onItemSelected() you'd also have to adjust position to take the default item into account, for example:

if (position == 0) {
    return;
} else {
    position = position - 1;
}
MyModel selected = items.get(position);
Piccadilly answered 13/1, 2017 at 15:2 Comment(3)
I like this idea, don't know why it had only one vote.Simmons
I haven't got it working yet...I'm just a newbie...I don't have "R.layout.my_spinner_row". Where do I get this from or what do I put instead?Irremeable
@TimCooper If you're using custom layout for Spinner items (like I was in this example), that's the layout resource file. If not, I guess you'd typically use Android's default android.R.layout.simple_spinner_item. Further reading: Spinner basics and custom Spinner layouts.Piccadilly
T
6

In my case, although size '2' is displayed in the spinner, nothing happens till some selection is done!

I have an xml file (data_sizes.xml) which lists all the spinner values.

<?xml version="1.0" encoding="utf-8"?>
<resources>
    <string-array name="chunks">
        <item>2</item>
        <item>4</item>
        <item>8</item>
        <item>16</item>
        <item>32</item>
    </string-array>
</resources>    

In main.xml file: Spinner element

<Spinner android:id="@+id/spinnerSize"  
android:layout_marginLeft="50px"
android:layout_width="fill_parent"                  
android:drawSelectorOnTop="true"
android:layout_marginTop="5dip"
android:prompt="@string/SelectSize"
android:layout_marginRight="30px"
android:layout_height="35px" /> 

Then in my java code, I added:

In my activity: Declaration

Spinner spinnerSize;
ArrayAdapter adapter;

In a public void function - initControls(): Definition

spinnerSize = (Spinner)findViewById(R.id.spinnerSize);
adapter = ArrayAdapter.createFromResource(this, R.array.chunks, android.R.layout.simple_spinner_item);
adapter.setDropDownViewResource(android.R.layout.simple_spinner_dropdown_item);
spinnerSize.setAdapter(adapter);
spinnerSize.setOnItemSelectedListener(new MyOnItemSelectedListener());

My spinner listener:

/* Spinner Listener */

class MyOnItemSelectedListener implements OnItemSelectedListener {

    public void onItemSelected(AdapterView<?> parent,
        View view, int pos, long id) {
        chunkSize = new Integer(parent.getItemAtPosition(pos).toString()).intValue();
    }
    public void onNothingSelected(AdapterView<?> parent) {
      // Dummy
    }
}
Trident answered 18/1, 2011 at 17:49 Comment(0)
O
5

Using a custom spinner layout like this:

<?xml version="1.0" encoding="utf-8"?>
<Spinner xmlns:android="http://schemas.android.com/apk/res/android"
          android:id="@+id/spinnerTarget"
          android:layout_width="fill_parent"
          android:layout_height="wrap_content"
          android:textSize="14dp"         
          android:textColor="#000000"/>

In the activity:

    // populate the list
    ArrayList<String> dataList = new ArrayList<String>();
    for (int i = 0; i < 4; i++) {
        dataList.add("Item");
    }

    // set custom layout spinner_layout.xml and adapter
    Spinner spinnerObject = (Spinner) findViewById(R.id.spinnerObject);
    ArrayAdapter<String> dataAdapter = new ArrayAdapter<String>(this, R.drawable.spinner_layout, dataList);
    dataAdapter.setDropDownViewResource(android.R.layout.simple_spinner_dropdown_item);
    spinnerObject.setAdapter(dataAdapter);
    spinnerObject.setOnTouchListener(new View.OnTouchListener() { 

        public boolean onTouch(View v, MotionEvent event) {
            // to set value of first selection, because setOnItemSelectedListener will not dispatch if the user selects first element
            TextView spinnerTarget = (TextView)v.findViewById(R.id.spinnerTarget);
            spinnerTarget.setText(spinnerObject.getSelectedItem().toString());

            return false;
        }

    });
    spinnerObject.setOnItemSelectedListener(new AdapterView.OnItemSelectedListener() {
                private boolean selectionControl = true;

                public void onItemSelected(AdapterView<?> parent, View view, int pos, long id) {
                    // just the first time
                    if(selectionControl){

                        // find TextView in layout 
                        TextView spinnerTarget = (TextView)parent.findViewById(R.id.spinnerTarget);
                        // set spinner text empty
                        spinnerTarget.setText("");
                        selectionControl = false;
                    }
                    else{
                        // select object
                    }
                }

                public void onNothingSelected(AdapterView<?> parent) {

                }
            });
Odious answered 7/7, 2012 at 6:30 Comment(1)
Adding the boolean is good work around. Thanks.Hephzipa
C
3

Merge this:

private long previousItemId = 0;

@Override
public long getItemId(int position) {
    long nextItemId = random.nextInt(Integer.MAX_VALUE);
    while(previousItemId == nextItemId) {
        nextItemId = random.nextInt(Integer.MAX_VALUE);
    }
    previousItemId = nextItemId;
    return nextItemId;
}

With this answer:

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

    private AdapterView.OnItemSelectedListener onItemSelectedListener;

    public SpinnerInteractionListener(AdapterView.OnItemSelectedListener selectedListener) {
        this.onItemSelectedListener = selectedListener;
    }

    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) {
            onItemSelectedListener.onItemSelected(parent, view, pos, id);
            userSelect = false;
        }
    }

    @Override
    public void onNothingSelected(AdapterView<?> parent) {
        if(userSelect) {
            onItemSelectedListener.onNothingSelected(parent);
            userSelect = false;
        }
    }
}
Curacy answered 6/6, 2016 at 16:34 Comment(0)
W
2

you can put the first cell in your array to be empty({"","some","some",...}) and do nothing if the position is 0;

public void onItemSelected(AdapterView<?> parent, View view, int position, long id) {
    if(position>0) {
        label.setText(MainActivity.questions[position - 1]);
    }
}
  • if you fill the array by xml file you can let the first item empty
Weissman answered 8/11, 2015 at 16:13 Comment(0)
D
2

My solution, in case you have a TextView as each row of the spinner:

    // TODO: add a fake item as the last one of "items"
    final ArrayAdapter<String> adapter=new ArrayAdapter<String>(..,..,items) 
      {
      @Override
      public View getDropDownView(final int position,final View convertView,final ViewGroup parent)
        {
        final View dropDownView=super.getDropDownView(position,convertView,parent);
        ((TextView)dropDownView.findViewById(android.R.id.text1)).setHeight(position==getCount()-1?0:getDimensionFromAttribute(..,R.attr.dropdownListPreferredItemHeight));
        dropDownView.getLayoutParams().height=position==getCount()-1?0:LayoutParams.MATCH_PARENT;
        return dropDownView;
        }
      }

    ...
    spinner.setAdapter(adapter);
    _actionModeSpinnerView.setSelection(dataAdapter.getCount()-1,false);



  public static int getDimensionFromAttribute(final Context context,final int attr)
    {
    final TypedValue typedValue=new TypedValue();
    if(context.getTheme().resolveAttribute(attr,typedValue,true))
      return TypedValue.complexToDimensionPixelSize(typedValue.data,context.getResources().getDisplayMetrics());
    return 0;
    }
Druse answered 4/12, 2015 at 23:45 Comment(0)
S
1

I assume that you want to have a Spinner with first empty invisible item (that is a strange feature of Spinner that cannot show a list without selecting an item). You should add a class that will contain data:

data class YourData(val id: Int, val name: String?)

This is the adapter.

class YourAdapter(
    context: Context,
    private val textViewResourceId: Int,
    private var items: ArrayList<YourData>
) : ArrayAdapter<YourData>(context, textViewResourceId, items) {

    private var inflater: LayoutInflater = context.getSystemService(
        Context.LAYOUT_INFLATER_SERVICE) as LayoutInflater

    override fun getCount(): Int = items.size + 1

    override fun getItem(position: Int): YourData? =
        if (position == 0) YourData(0, "") else items[position - 1]

    override fun getItemId(position: Int): Long = position.toLong()

    override fun getView(position: Int, convertView: View?, parent: ViewGroup): View =
        if (position == 0) {
            getFirstTextView(convertView)
        } else {
            getTextView(convertView, parent, position - 1)
        }

    override fun getDropDownView(position: Int, convertView: View?, parent: ViewGroup): View =
        getView(position, convertView, parent)

    private fun getFirstTextView(convertView: View?): View {
        // Just simple TextView as initial selection.
        var textView: TextView? = convertView as? TextView
        val holder: FirstViewHolder
        if (textView?.tag !is FirstViewHolder) {
            textView = TextView(context) // inflater.inflate(R.layout.your_text, parent, false) as TextView
            textView.height = 0 // Hide first item.
            holder = FirstViewHolder()
            holder.textView = textView
            textView.tag = holder
        }
        return textView
    }

    private fun getTextView(
        convertView: View?,
        parent: ViewGroup,
        position: Int
    ): TextView {
        var textView: TextView? = convertView as? TextView
        val holder: ViewHolder
        if (textView?.tag is ViewHolder) {
            holder = textView.tag as ViewHolder
        } else {
            textView = inflater.inflate(textViewResourceId, parent, false) as TextView
            holder = ViewHolder()
            holder.textView = textView
            textView.tag = holder
        }
        holder.textView.text = items[position].name

        return textView
    }

    private class FirstViewHolder {
        lateinit var textView: TextView
    }

    private class ViewHolder {
        lateinit var textView: TextView
    }
}

To create:

YourAdapter(context!!, R.layout.text_item, ArrayList())

To add items:

private fun fill(items: List<YourData>, adapter: YourAdapter) {
    adapter.run {
        clear()
        addAll(items)
        notifyDataSetChanged()
    }
}

When you load items to your Spinner with that fill() command, you should know, that indices are also incremented. So if you wish to select 3rd item, you should now select 4th: spinner?.setSelection(index + 1)

Selig answered 20/11, 2018 at 7:49 Comment(0)
M
0

Base on @Jonik answer I have created fully functional extension to ArrayAdapter

class SpinnerArrayAdapter<T> : ArrayAdapter<T> {

    private val selectText : String
    private val resource: Int
    private val fieldId : Int
    private val inflater : LayoutInflater

    constructor(context: Context?, resource: Int, objects: Array<T>, selectText : String? = null) : super(context, resource, objects) {
        this.selectText = selectText ?: context?.getString(R.string.spinner_default_select_text) ?: ""
        this.resource = resource
        this.fieldId = 0
        this.inflater = LayoutInflater.from(context)
    }
    constructor(context: Context?, resource : Int, objects: List<T>, selectText : String? = null) : super(context, resource, objects) {
        this.selectText = selectText ?: context?.getString(R.string.spinner_default_select_text) ?: ""
        this.resource = resource
        this.fieldId = 0
        this.inflater = LayoutInflater.from(context)
    }
    constructor(context: Context?, resource: Int, textViewResourceId: Int, objects: Array<T>, selectText : String? = null) : super(context, resource, textViewResourceId, objects) {
        this.selectText = selectText ?: context?.getString(R.string.spinner_default_select_text) ?: ""
        this.resource = resource
        this.fieldId = textViewResourceId
        this.inflater = LayoutInflater.from(context)
    }
    constructor(context: Context?, resource : Int, textViewResourceId: Int, objects: List<T>, selectText : String? = null) : super(context, resource, textViewResourceId,  objects) {
        this.selectText = selectText ?: context?.getString(R.string.spinner_default_select_text) ?: ""
        this.resource = resource
        this.fieldId = textViewResourceId
        this.inflater = LayoutInflater.from(context)
    }

    override fun getDropDownView(position: Int, convertView: View?, parent: ViewGroup?): View {
        if(position == 0) {
            return initialSelection(true)
        }

        return createViewFromResource(inflater, position -1, convertView, parent, resource)
    }

    override fun getView(position: Int, convertView: View?, parent: ViewGroup?): View {
        if(position == 0) {
            return initialSelection(false)
        }
        return createViewFromResource(inflater, position -1, convertView, parent, resource)
    }

    override fun getCount(): Int {
        return super.getCount() + 1 // adjust for initial selection
    }

    private fun initialSelection(inDropDown: Boolean) : View {
        // Just simple TextView as initial selection.
        val view = TextView(context)
        view.setText(selectText)
        view.setPadding(8, 0, 8, 0)

        if(inDropDown) {
            // Hide when dropdown is open
            view.height = 0
        }
        return view
    }

    private fun createViewFromResource(inflater: LayoutInflater, position: Int,
                                       @Nullable convertView: View?, parent: ViewGroup?, resource: Int): View {
        val view: View
        val text: TextView?

        if (convertView == null || (convertView is TextView)) {
            view = inflater.inflate(resource, parent, false)
        } else {
            view = convertView
        }

        try {
            if (fieldId === 0) {
                //  If no custom field is assigned, assume the whole resource is a TextView
                text = view as TextView
            } else {
                //  Otherwise, find the TextView field within the layout
                text = view.findViewById(fieldId)

                if (text == null) {
                    throw RuntimeException("Failed to find view with ID "
                            + context.getResources().getResourceName(fieldId)
                            + " in item layout")
                }
            }
        } catch (e: ClassCastException) {
            Log.e("ArrayAdapter", "You must supply a resource ID for a TextView")
            throw IllegalStateException(
                    "ArrayAdapter requires the resource ID to be a TextView", e)
        }

        val item = getItem(position)
        if (item is CharSequence) {
            text.text = item
        } else {
            text.text = item!!.toString()
        }

        return view
    }
Madelinemadella answered 10/8, 2018 at 8:34 Comment(0)
Y
0

Explanation- We can send a Dummy Value in first Parameter of our arrayList to spinner Adapter. Then send position(0 or any in between of arrayList) of Our dummy value in arrayList to SpinnerAdapter constructor .

public void addDataToList(LstQRTrackingSummary qrTrackingSummary) {
        SpinnerMenu mSpinnerMenuzero = new SpinnerMenu();
        mSpinnerMenuzero.setName("Select Action");
        spinner_list.add(mSpinnerMenuzero);

        if (qrTrackingSummary.getOQRTrackingButtons().getBtnToDispatch()) {
            SpinnerMenu mSpinnerMenu = new SpinnerMenu();
            mSpinnerMenu.setName("Dispatch");
            mSpinnerMenu.setmDrawable(ResourcesCompat.getDrawable(context.getResources(),R.drawable.ic_dispatch,null));
            spinner_list.add(mSpinnerMenu);
        }

        if (qrTrackingSummary.getOQRTrackingButtons().getBtnForwardToDispatch()) {
            SpinnerMenu mSpinnerMenu = new SpinnerMenu();
            mSpinnerMenu.setName("Forward To Dispatch");
            mSpinnerMenu.setmDrawable(ResourcesCompat.getDrawable(context.getResources(), R.drawable.ic_forward_to_dispatch, null));
            spinner_list.add(mSpinnerMenu);
        }
  }

then in spinner adapter class hide it in same way as i did and its done.

public class SpinnerAdapter extends ArrayAdapter<SpinnerMenu> {

private Context context;
private ArrayList<SpinnerMenu> menuList;
private LayoutInflater inflater;
private SpinnerMenu spinnerMenu;
public Integer positionToHide ;

public SpinnerAdapter(Context context, Integer positionToHide,ArrayList<SpinnerMenu> menuList) {
    super(context, 0, menuList);
    this.context = context;
    this.positionToHide = positionToHide ;
    inflater = LayoutInflater.from(context);
    this.menuList = menuList;
}

@NonNull
@Override
public View getView(int position, @Nullable View convertView, @NonNull ViewGroup parent) {
    return getCustomView(position, convertView, parent);
}

@Override
public View getDropDownView(int position, @Nullable View convertView, @NonNull ViewGroup parent) {
    return getCustomView(position, convertView, parent);
}

private View getCustomView(int position, View convertView , ViewGroup parent) {
    View view = null ;
    if (view == null) {
        view = inflater.inflate(R.layout.spinner_baseview, parent, false);
    }

    ImageView spin_image = view.findViewById(R.id.spinner_image);
    TextView spin_text = view.findViewById(R.id.spinner_text);
    spinnerMenu = menuList.get(position);

    if (position == positionToHide) {
        TextView mytext = new TextView(context);
        mytext.setVisibility(View.GONE);
        mytext.setHeight(0);
        view = mytext;
        view.setVisibility(View.GONE);
    } else {
        spin_image.setImageDrawable(spinnerMenu.getmDrawable());
        spin_text.setText(spinnerMenu.getName());
    }
    return view;
}

}

Yaker answered 25/3, 2021 at 11:59 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.