Matthias Robbers Matthias Robbers - 3 months ago 102
Android Question

Spinner does not wrap text -- is this an Android bug?

If the text of a

Spinner
item is too long to fit into a single line, the text is not wrapped but cut off. This is only the case for API level >= 11. Here are screenshots of Android 4.2.2 (left) which shows the wrong behavior and Android 2.3.3 (right) where it looks as expected.




android:singleLine="false"
simply gets ignored here. So as all other tries like
android:lines
,
android:minLines
, etc. The
TextView
somehow seems to be much wider than the window width.

I saw other people having the same problem, but no one could find a solution. So, is this a system bug? I don't think this inconsistency between the OS versions can be intended.




Please note:



There were some answers suggesting relatively simple solutions.


  • Writing a custom
    Adapter
    and overriding
    getView()
    as well as
    getDropDownView()
    . This is not the solution here, because at this point, there is still the original problem: How does the layout have to look like to handle proper line wrapping?

  • Wrapping the
    TextView
    of the drop down view into a parent
    ViewGroup
    . Does not work with
    android:layout_width="match_parent"
    because the width of the parent strangely seems to be unlimited.

  • Giving the drop down view a fixed width. This is not suitable with the different widths the
    Spinner
    can have.

  • And of course, no solution is to manually insert
    \n
    s anywhere into the text.






Reproduce with the following code:



UPDATE: I also uploaded this as a sample project on GitHub: Download

/res/values/arrays.xml:

<string-array name="items">
<item>Lorem ipsum dolor sit amet, consetetur sadipscing elitr, sed diam nonumy eirmod tempor invidunt.</item>
<item>At vero eos et accusam et justo duo dolores et ea rebum. Stet clita kasd gubergren, no sea takimata sanctus est.</item>
</string-array>


/res/layout/spinner_item.xml:

<TextView xmlns:android="http://schemas.android.com/apk/res/android"
android:id="@android:id/text1"
style="?android:attr/spinnerDropDownItemStyle"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:ellipsize="none"
android:minHeight="?android:attr/listPreferredItemHeight"
android:singleLine="false" />


Set
Adapter
:

spinner.setAdapter(ArrayAdapter.createFromResource(this,
R.array.items,
R.layout.spinner_item));

Answer

In holo theme spinner by default uses dropdown mode. And all moves with overriding default styles just move to switching spinner mode to dialog mode which succesfully wraps multiline text as in api 11. Instead you can create spinner with new Spinner(context, Spinner.MODE_DIALOG) or in xml: android:spinnerMode="dialog". But it's not resolve the problem, because it's dialog, not dropdown.

I have found another solution for this trouble: Override getDropDownView method in ArrayAdapter and put setSingleLine(false) in post method of view. So when view completly created it wraps the text to appropriate lines.

 @Override
    public View getDropDownView(final int position, View convertView, ViewGroup parent) {
        if (convertView == null) {
            convertView = new TextView(_context);
        }

        TextView item = (TextView) convertView;
        item.setText("asddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddd");
        final TextView finalItem = item;
        item.post(new Runnable() {
            @Override
            public void run() {
                finalItem.setSingleLine(false);
            }
        });
        return item;
    }

UPDATE:

And here is another answer.

Manually wrap listview in PopupWindow and show it udner TextView on click and hide it on listItem click. Simple implimentation just to show idea:

public class MySpinner extends TextView {
private PopupWindow _p;
private ListView _lv;
public MySpinner(Context context) {
    super(context);
    init();
}
public MySpinner(Context context, AttributeSet attributeSet){
    super(context, attributeSet);
    init();
}

private void init(){
    setBackgroundResource(R.drawable.spinner_background);
    final List<String> list = new ArrayList<String>();
    list.add("Very long text AAAAAAAAAAAAAAAA");
    list.add("1 Very long text AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA");
    list.add("2 Very long text A");
    list.add("3 Very long text AAAAAAAAA");

    setMinimumWidth(100);
    setMaxWidth(200);

    _lv = new ListView(getContext());
    _lv.setAdapter(new ArrayAdapter<String>(getContext(), R.layout.simple_list_item_1, list));
    _lv.setOnItemClickListener(new AdapterView.OnItemClickListener() {
        @Override
        public void onItemClick(AdapterView<?> adapterView, View view, int i, long l) {
            _p.dismiss();
            setText(list.get(i));
        }
    });

    setOnClickListener(new OnClickListener() {
        @Override
        public void onClick(View view) {

            _p = new PopupWindow(getContext());
            _p.setContentView(_lv);
            _p.setWidth(getWidth());
            _p.setHeight(ViewGroup.LayoutParams.WRAP_CONTENT);
            _p.setTouchable(true);
            _p.setFocusable(true);
            _p.setOutsideTouchable(true);
            _p.showAsDropDown(view);
        }
    });
}

}