How to set Spinner default value to null?

后端 未结 10 1644
天命终不由人
天命终不由人 2020-11-29 02:36

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 be

相关标签:
10条回答
  • 2020-11-29 03:01

    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.

    0 讨论(0)
  • 2020-11-29 03:01

    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)

    0 讨论(0)
  • 2020-11-29 03:03

    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
        }
    
    0 讨论(0)
  • 2020-11-29 03:14

    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).

    0 讨论(0)
  • 2020-11-29 03:16

    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
    0 讨论(0)
  • 2020-11-29 03:18

    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);
    
    0 讨论(0)
提交回复
热议问题