Android Spinner -- how to size to currently selected item?
Asked Answered
M

5

6

It appears that a spinner is sized to the longest item given in its adapter. This is a good behavior for the majority of cases, but it is undesirable in my particular case.

Is this possible to turn this off? My guess is no, by looking at Spinner.onMeasure() in the source, but figured I'd ask.

Moxa answered 1/11, 2012 at 2:51 Comment(0)
M
4

I achieved this now by subclassing Spinner:

public class SpinnerWrapContent extends IcsSpinner {
    private boolean inOnMeasure;

    protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {       
         inOnMeasure = true;
         super.onMeasure(widthMeasureSpec, heightMeasureSpec);
         inOnMeasure = false;
    }

    public boolean isInOnMeasure() {
        return inOnMeasure;
    }
}

Then in my SpinnerAdapter's getView(), I used the currently selected position if I am called from onMeasure():

    public View getView(int position, View convertView, ViewGroup parent) {
        View view;

        if (convertView != null)
            view = convertView;
        else {
            int fixedPosition = (spinner.isInOnMeasure() ? spinner.getSelectedItemPosition() : position);

            // Here create view for fixedPosition
        }

        return view;
    }
Moxa answered 1/11, 2012 at 7:52 Comment(0)
S
1

This worked for me. Important part is this. Put the below above code is your adapter and use selectedItemPosition for selecting text from objects array.

int selectedItemPosition = position;
    if (parent instanceof AdapterView) {
        selectedItemPosition = ((AdapterView) parent)
                .getSelectedItemPosition();
    }

Example is given below.

public View getView(int position, View convertView, ViewGroup parent) {
    LayoutInflater inflater = getLayoutInflater();
    final View spinnerCell;
    if (convertView == null) {
        // if it's not recycled, inflate it from layout
        spinnerCell = inflater.inflate(R.layout.layout_spinner_cell, parent, false);
    } else {
        spinnerCell = convertView;
    }
    int selectedItemPosition = position;
    if (parent instanceof AdapterView) {
        selectedItemPosition = ((AdapterView) parent)
                .getSelectedItemPosition();
    }

    TextView title = (TextView) spinnerCell.findViewById(R.id.spinnerTitle);
    title.setText(titles[selectedItemPosition]);
    return spinnerCell;
}

If you need an explanation follow this link: http://coding-thoughts.blogspot.in/2013/11/help-my-spinner-is-too-wide.html

Salable answered 7/2, 2016 at 8:13 Comment(0)
S
1

If anyone is looking for a Kotlin Answer, here is how you do it.

class DynamicSizeSpinner : androidx.appcompat.widget.AppCompatSpinner {

    var inOnMeasure = false
        private set

    constructor(context: Context) : super(context)
    constructor(context: Context, attrs: AttributeSet) : super(context, attrs)
    constructor(context: Context, attrs: AttributeSet, defStyleAttr: Int) : super(context, attrs, defStyleAttr)

    override fun onMeasure(widthMeasureSpec: Int, heightMeasureSpec: Int) {
        inOnMeasure = true
        super.onMeasure(widthMeasureSpec, heightMeasureSpec)
        inOnMeasure = false
    }
}
class SpinnerArrayAdapter(context: Context,@LayoutRes layout: Int, val entries: List<String>) : ArrayAdapter<String>(context, layout, entries) {

    override fun getView(position: Int, convertView: View?, parent: ViewGroup): View {
        val selectedItemPosition = when (parent) {
            is AdapterView<*> -> parent.selectedItemPosition
            is DynamicSizeSpinner -> parent.selectedItemPosition
            else -> position
        }
        return makeLayout(selectedItemPosition, convertView, parent, R.layout.simple_spinner_dropdown_item_custom)
    }

    override fun getDropDownView(position: Int, convertView: View?, parent: ViewGroup): View {
        return makeLayout(position, convertView, parent, R.layout.simple_spinner_dropdown_item_custom)
    }

    private fun makeLayout(position: Int, convertView: View?, parent: ViewGroup, layout: Int): View {
        val view = convertView ?: LayoutInflater.from(context).inflate(layout, parent, false)
        if (position != -1) {
            (view as? TextView)?.text = entries[position]
        }
        return view
    }
}

Use the DynamicSizeSpinner as you would use the default Spinner in your XML/JAVA/Kotlin code.

P.S don't forget to read the original article.

Saunders answered 5/8, 2021 at 8:51 Comment(1)
No need to introduce inOnMeasure in your DynamicSizeSpinner, the field is not used by SpinnerArrayAdapterVashti
A
0

If your're looking to just make the Spinner shorter, it's an easy fix.

Generally you can change the height and width of any view by giving it a weight, or setting it's layout_width and layout_height:

<Spinner
    android:id="@+id/shortenedSpinner"
    android:layout_width="100dp"
    android:layout_height="50dp" />

This will force the spinner to be shorter

If you want the drop down view to be shorter, that is different. In this case, I suppose you could supply a custom row in the spinner adapter's getDropDownView() method which has the same changes as stated above.

Accroach answered 1/11, 2012 at 3:5 Comment(3)
Hi, thanks for your answer, but I don't want to make the spinner a fixed width. I want it to change its size depending on the selected View, a WRAP_CONTENT effect. Currently, the code in onMeasure() sizes it to the largest View returned by the adapter.Moxa
Okay, how about resetting the fixed width every time a selection is made. Set the width based on the width of the selected text. This could be estimated by using canvas font measuring as stated in #7713212Accroach
Thanks, I answered my own question below.Moxa
V
0

Here is more elegant almost one-liner solution:

class ResizingArrayAdapter<T>(context: Context, @LayoutRes layoutResId: Int, @IdRes textViewResourceId: Int, objects: List<T>) :
    ArrayAdapter<T>(context, layoutResId, textViewResourceId, objects) {

    override fun getView(position: Int, convertView: View?, parent: ViewGroup): View =
        super.getView(if (parent is AdapterView<*>) parent.selectedItemPosition else position, convertView, parent)
}
Vashti answered 9/2, 2022 at 10:55 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.