Set spinner width to current item width
Asked Answered
K

3

7

By default the spinner width is set to fit the largest item in the dropdown but I want it to be the same width as the selected item.

<android.widget.Spinner
    android:id="@+id/tab_spinner"
    android:layout_width="wrap_content"
    android:layout_height="match_parent"
    android:entries="@array/countries" />

enter image description here

As you can see in the right side image, the spinner is way too long, because of the long item in the list.

How can I resize it to the selected item width ?

Kimberli answered 12/10, 2016 at 23:28 Comment(0)
U
20

By default, Spinner will try to measure some of your dropdown views and use the max width found. This happens in Spinner#measureContentWidth(), a protected method of Spinner called in Spinner#onMeasure().

One way to solve the issue is to make sure your SpinnerAdapter#getView() method always uses Spinner#getSelectedItemPosition() for its position argument.

I can think of two possible solutions:

  1. Creating a custom spinner with a wrapper adapter (I prefer this solution)
  2. Or adapting your custom adapter


1. Creating a custom spinner with a wrapper adapter

Make sure to use this in your xml layout, instead of a normal <Spinner>.

Be careful that DynamicWidthSpinner#getAdapter() will return a WrapperSpinnerAdapter; use its getBaseAdapter() method to access to your adapter.

public class DynamicWidthSpinner extends Spinner {

    public DynamicWidthSpinner(Context context) {
        super(context);
    }

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

    public DynamicWidthSpinner(Context context, AttributeSet attrs, int defStyleAttr) {
        super(context, attrs, defStyleAttr);
    }


    public void setAdapter(SpinnerAdapter adapter) {
        super.setAdapter(adapter != null ? new WrapperSpinnerAdapter(adapter) : null);
    }


    public final class WrapperSpinnerAdapter implements SpinnerAdapter {

        private final SpinnerAdapter mBaseAdapter;


        public WrapperSpinnerAdapter(SpinnerAdapter baseAdapter) {
            mBaseAdapter = baseAdapter;
        }


        public View getView(int position, View convertView, ViewGroup parent) {
            return mBaseAdapter.getView(getSelectedItemPosition(), convertView, parent);
        }

        public final SpinnerAdapter getBaseAdapter() {
            return mBaseAdapter;
        }

        public int getCount() {
            return mBaseAdapter.getCount();
        }

        public View getDropDownView(int position, View convertView, ViewGroup parent) {
            return mBaseAdapter.getDropDownView(position, convertView, parent);
        }

        public Object getItem(int position) {
            return mBaseAdapter.getItem(position);
        }

        public long getItemId(int position) {
            return mBaseAdapter.getItemId(position);
        }

        public int getItemViewType(int position) {
            return mBaseAdapter.getItemViewType(position);
        }

        public int getViewTypeCount() {
            return mBaseAdapter.getViewTypeCount();
        }

        public boolean hasStableIds() {
            return mBaseAdapter.hasStableIds();
        }

        public boolean isEmpty() {
            return mBaseAdapter.isEmpty();
        }

        public void registerDataSetObserver(DataSetObserver observer) {
            mBaseAdapter.registerDataSetObserver(observer);
        }

        public void unregisterDataSetObserver(DataSetObserver observer) {
            mBaseAdapter.unregisterDataSetObserver(observer);
        }
    }
}


2. Adapting your custom adapter

Be careful that parent in getView() might not always be a Spinner.

private class SimpleSpinnerAdapter extends BaseAdapter {

    private LayoutInflater mInflater;
    private int mResource;


    public SimpleSpinnerAdapter(Context context, int resource) {
        mInflater = LayoutInflater.from(context);
        mResource = resource;
    }


    @Override
    public View getView(int position, View convertView, ViewGroup parent) {
        return createViewFromResource(
                mInflater,
                ((Spinner) parent).getSelectedItemPosition(),
                convertView,
                parent,
                mResource);
    }

    @Override
    public View getDropDownView(int position, View convertView, ViewGroup parent) {
        return createViewFromResource(
                mInflater,
                position,
                convertView,
                parent,
                mResource);
    }

    protected View createViewFromResource(LayoutInflater inflater, int position, 
                                          View convertView, ViewGroup parent,
                                          int resource) {
        View view;
        if (convertView == null) {
            view = inflater.inflate(resource, parent, false);
        } else {
            view = convertView;
        }

        bindView(position, view);

        return view;
    }

    protected void bindView(int position, View view) {
        // Bind your view.
    }

    // getCount(), getItem(), and getItemId() methods.
}
Uncouple answered 23/11, 2016 at 12:10 Comment(4)
first option is the most flexible oneLimburg
The first option is not working for me. When I implement it (and I had to tweak it for kotlin and missing code in the getView function) the Spinner does not respond to tap events and the dropdown arrow disappeared. I'm going to put my current code in a response below. I'm using a custom TextView as the spinner itemSelection
@Uncouple i put my code up in a new SO question here: #56998698Selection
Not working any of the above solutions @UncoupleNaval
W
-1
  ViewGroup.LayoutParams spinnerLayoutParams = spinner.getLayoutParams();
    spinnerLayoutParams.width -= 1;
   spinner.setLayoutParams(spinnerLayoutParams);

Do this is on item selected

Wetmore answered 18/4, 2017 at 18:31 Comment(1)
the value "-1" is MATCH_PARENT . Why would this help?Geotropism
O
-4

Setting the spinners XML to:

android:layout_width="wrap_content"

worked for me.

Orsa answered 12/10, 2016 at 23:54 Comment(1)
Sorry forgot to post my code, but it was already like that and changed nothing for me...Kimberli

© 2022 - 2024 — McMap. All rights reserved.