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

AndroidAndroid LayoutSpinnerAndroid Spinner

Android Problem Overview


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.

http://i.stack.imgur.com/Marym.png" width="320" /> http://i.stack.imgur.com/xsXtn.png" width="320" />

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 \ns 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));

Android Solutions


Solution 1 - Android

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 completely 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 under TextView on click and hide it on listItem click.

Simple implementation 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);
            }
        });
    }
}

Solution 2 - Android

Only a combination of solutions here worked for me (tested on Android 5.1 too) :

spinner_item.xml

<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
             android:layout_width="match_parent"
             android:layout_height="wrap_content">

  <TextView
    android:id="@android:id/text1"
    style="?android:attr/spinnerItemStyle"
    android:layout_width="match_parent"
    android:layout_height="wrap_content"
    android:singleLine="false"
    android:textAlignment="inherit"/>
</LinearLayout>

code

  final ArrayAdapter<String> spinnerArrayAdapter=new ArrayAdapter<String>(activity,R.layout.spinner_item,android.R.id.text1,spinnerItemsList)
  {
  @Override
  public View getDropDownView(final int position,final View convertView,final ViewGroup parent)
    {
    final View v=super.getDropDownView(position,convertView,parent);
    v.post(new Runnable()
    {
    @Override
    public void run()
      {
      ((TextView)v.findViewById(android.R.id.text1)).setSingleLine(false);
      }
    });
    return v;
    }
  };
  spinnerArrayAdapter.setDropDownViewResource(android.R.layout.simple_spinner_dropdown_item);

Solution 3 - Android

I have solved this problem by switching to dialog-style spinner:

<Spinner
  ...
android:spinnerMode="dialog" />

Solution 4 - Android

Adding a LinearLayout around the TextView allows the text to wrap correctly.

Layout (common_domainreferencemodel_spinner_item.xml):

<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
    android:layout_height="wrap_content"
    android:layout_width="match_parent"
    android:padding="4dp">

       <TextView
            android:id="@+id/nameTextView"
            android:singleLine="false"
            android:layout_width="match_parent"
            android:layout_height="wrap_content" />

</LinearLayout>

Adapter:

public class DomainReferenceModelAdapter extends ArrayAdapter<DomainReferenceModel> {

    private List<DomainReferenceModel> objects;
    private LayoutInflater inflater;
    private int oddRowColor = Color.parseColor("#E7E3D1");
    private int evenRowColor = Color.parseColor("#F8F6E9");

    public DomainReferenceModelAdapter(Context context, int resource, List<DomainReferenceModel> objects) {
        super(context, resource, objects);
        this.objects = objects;
        this.inflater = (LayoutInflater) context.getSystemService(Context.LAYOUT_INFLATER_SERVICE);
    }

    static class ViewHolder {
        public TextView nameTextView;
    }

    @Override
    public View getView(int position, View convertView, ViewGroup parent) {
        return getViewInternal(position, convertView, parent, false);
    }

    @Override
    public View getDropDownView(int position, View convertView, ViewGroup parent) {
        return getViewInternal(position, convertView, parent, true);
    }

    private View getViewInternal(int position, View convertView, ViewGroup parent, boolean isDropdownView) {
        View view = convertView;
        if (view == null) {
            view = inflater.inflate(R.layout.common_domainreferencemodel_spinner_item, null);
            ViewHolder viewHolder = new ViewHolder();
            viewHolder.nameTextView = (TextView) view.findViewById(R.id.nameTextView);
            view.setTag(viewHolder);
        }
        if (isDropdownView) {
            view.setBackgroundColor(position % 2 == 0 ? evenRowColor : oddRowColor);
        }
        ViewHolder holder = (ViewHolder) view.getTag();
        DomainReferenceModel model = objects.get(position);
        holder.nameTextView.setText(model.getName());
        return view;
    }

}

Solution 5 - Android

Achieving multi-line drop-down items on a Spinner while using a Holo theme isn't possible, from what I've tried.

A workaround solution is to:

  • create a style for the Spinner that doesn't inherit from Holo. This will enable multi-line drop-down items.
  • Style the Spinner 'manually' so it looks like it's Holo-themed.

This produces (showing closed and open states):

enter image description here

Details of implementation:

There is no way to inherit from a Holo theme on the Spinner and show multiple lines in the Spinner drop-down item as far as I can tell, even if we set the drop-down item's TextView singleLine attribute to false and supply a custom layout. I've also tried keeping the Holo style but altering the

android:spinnerStyle
android:spinnerItemStyle 
android:spinnerDropDownItemStyle 

styles attributes (example of using these attributes here) but I couldn't make it produce a multi-line result.

However, if we override the style for the Spinner and don't inherit spinnerStyle from Holo:

 <style name="AppTheme" parent="android:Theme.Holo.Light">
    <item name="android:spinnerStyle">@style/spinnerStyle</item>
</style>

<--no parent attribute-->
 <style name="spinnerStyle">
    <item name="android:clickable">true</item>
</style>

then the drop-down item will support showing multiple lines. But now we have lost the Holo theme on the Spinner and the closed state looks like a TextView not a Spinner with no arrow or visual clue it's a Spinner. If we instead set spinnerStyle parent to: parent="android:style/Widget.Spinner:

<style name="spinnerStyle" parent="android:style/Widget.Spinner">
    <item name="android:clickable">true</item>
</style>

the Spinner closed state will show the arrow but will be styled like the grey pre-Holo Spinner which looks out of place in a Holo app.

So, a possible solution is then:

  • Override the theme for spinnerStyle and don't use Holo for parent. This will enable multi-line text in the DropDown items.
  • Change the Spinner background to look like it inherits the Holo theme.

Here's an example:

Create a basic Activity:

@Override
protected void onCreate(Bundle savedInstanceState) {
	super.onCreate(savedInstanceState);
	setContentView(R.layout.activity_main);
	
	Spinner spinner = (Spinner)findViewById(R.id.styled_spinner);
	
	spinner.setAdapter(ArrayAdapter.createFromResource(this,
            R.array.items,
            R.layout.spinner_item));		
}

Activity layout:

<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:tools="http://schemas.android.com/tools"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    android:padding="50dip"
    tools:context=".MainActivity" >

    <Spinner
        android:id="@+id/styled_spinner"
        android:layout_width="match_parent"
        android:layout_height="wrap_content"/>

</LinearLayout>

styles:

<resources xmlns:android="http://schemas.android.com/apk/res/android">
    <style name="AppTheme" parent="android:Theme.Holo.Light">
        <item name="android:spinnerStyle">@style/spinnerStyle</item>
    </style>
    <style name="spinnerStyle">
        <item name="android:clickable">true</item>
        <item name="android:background">@drawable/spinner_background_holo_light</item>
    </style>
</resources>

in drawable folder, place spinner_background_holo_light:

<selector xmlns:android="http://schemas.android.com/apk/res/android">
    <item android:state_enabled="false"
        android:drawable="@drawable/spinner_disabled_holo_light" />
    <item android:state_pressed="true"
        android:drawable="@drawable/spinner_pressed_holo_light" />
    <item android:state_pressed="false" android:state_focused="true"
        android:drawable="@drawable/spinner_focused_holo_light" />
    <item android:drawable="@drawable/spinner_default_holo_light" />
</selector>

and include these drawables in your drawables-hdpi folder:

enter image description here spinner_default_holo_light.9.png

enter image description here spinner_disabled_holo_light.9.png

enter image description here spinner_focused_holo_light.9.png

enter image description here spinner_pressed_holo_light.9.png

This produces a spinner with Holo-themed closed state and multi-line items, as shown in the screenshots above.

The drop-down items in this example are not Holo-themed but perhaps it's an acceptable trade-off if multi-line display of the drop down items is really important.

In this example, android:minSdkVersion was set to 14 and android:targetSdkVersion to 17 in Manifest.

Holo graphics and the spinner_background_holo_light.xml code come from HoloEverywhere Copyright (c) 2012 Christophe Versieux, Sergey Shatunov. See linked-to github project for license details.

Solution 6 - Android

I faced to the same problem. I want to see 2 lines in dropdown list of spinner, but all solutions that I've found seem to me unreasonable to resolve such simple problem. I investigate Spinner source code and I found If we use custom .xml with attribute android:singleLine="false"

<CheckedTextView xmlns:android="http://schemas.android.com/apk/res/android"
    android:id="@+id/multiline_spinner_text_view"
    android:layout_width="fill_parent"
    android:layout_height="?android:attr/listPreferredItemHeight"
    android:singleLine="false" />

and default ArrayAdapter, following code executed in ListPopupWindow everytime

    @Override
       View More obtainView(int position, boolean[] isScrap) {
            View view = super.obtainView(position, isScrap);

           if (view instanceof TextView) {
                ((TextView) view).setHorizontallyScrolling(true);
            }

            return view;        
}

and that's why we see only one string line per list row, it 's actually scrolling.

To resolve such problem our view should be not instance of TextView, just put your TextView inside FrameLayout or LinearLayout.

   <LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
        android:layout_width="match_parent"
        android:layout_height="wrap_content" >
    
        <CheckedTextView
            android:id="@+id/multiline_spinner_text_view"
            android:layout_width="fill_parent"
            android:layout_height="?android:attr/listPreferredItemHeight"
            android:singleLine="false" />
    
    </LinearLayout>

and

ArrayAdapter<String> adapter = new ArrayAdapter<String>(this, 
               R.layout.multiline_spinner_dropdown_item,R.id.multiline_spinner_text_view,
                awasomeListValues);

This solution works in both spinner mode: MODE_DROPDOWN and MODE_DROPDOWN.Hope it helps you!

Solution 7 - Android

I think there is a bug on android. You could try this. Remove the spaces from the text and then display it would work fine. If the length of the textview is < that of the string, it ignores all the characters after the space. For a work-around you could try this :

add a file to res/layout folder named multiline_spinner_dropdown_item.xml with the sample code:

<CheckedTextView xmlns:android="http://schemas.android.com/apk/res/android"
style="?android:attr/spinnerDropDownItemStyle"
android:singleLine="false"
android:layout_width="match_parent"
android:layout_height="?android:attr/listPreferredItemHeight"
android:ellipsize="marquee" />

and when you are creating the spinner create it from this layout.

Something like :

ArrayAdapter.createFromResource(this, items, R.layout.multiline_spinner_dropdown_item);

Basically, copy the android.R.layout.simple_spinner_dropdown_item layout into the project and modify the layout by setting singleLine attribute to false in CheckedTextView.

Solution 8 - Android

i just found it that android has existing style for this case

final Spinner pelanggaran = (Spinner) findViewById(R.id.pelanggaran);
ArrayAdapter<CharSequence> pelanggaran_adapter = ArrayAdapter.createFromResource(this,R.array.pelanggaran_array, android.R.layout.simple_expandable_list_item_1);
pelanggaran_adapter.setDropDownViewResource(android.R.layout.simple_expandable_list_item_1);
pelanggaran.setAdapter(pelanggaran_adapter);

hope it solved you problem.

Solution 9 - Android

Reading this answer: https://stackoverflow.com/questions/29291526/textview-casting-error-android-widget-linearlayout-cannot-be-cast-to-android-w#29293776 and this topic, I could solve this problem: We need a LinearLayout wrapping the TextView (The spinner text) to avoid the text getting out from the screen, but we will have some problems to solve. To start, create the layout (I called it spinner_dd_item.xml):

    <LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
        xmlns:tools="http://schemas.android.com/tools"
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:orientation="vertical">

        <TextView
            android:id="@+id/simple_spinner_dropdown"
            android:layout_width="match_parent"
            android:layout_height="match_parent"
            android:paddingBottom="5dp"
            android:paddingLeft="10dp"
            android:paddingRight="10dp"
            android:paddingTop="5dp"
            android:textColor="@color/colorAccent"
            tools:text="Hello" />
    </LinearLayout>

The next step is creating an ArrayAdapter instance to set it to the spinner:

    ArrayAdapter<CharSequence> arrayAdapter = new ArrayAdapter<CharSequence>(getActivity(), R.layout.spinner_dd_item,
            R.id.simple_spinner_dropdown, hashmapToString(hashMap, keys)) {
        @Override
        public View getDropDownView(int position, View convertView, ViewGroup parent) {
            return getView(position, convertView, parent);
        }
    };
    spinner.setAdapter(arrayAdapter);

Don't forget adding the layout name and the TextView id on the ArrayAdapter because we added the LinearLayout and we need to specify the TextView, and override getDropDownView to get the view that displays the data at the specified position in the data set. Now, we can see the spinner working nice on newer and older android versions

Solution 10 - Android

Here's what I did to make it work:

ArrayAdapter<KeyValue> adapter = new ArrayAdapter<>(getContext(), R.layout.simple_dropdown_item_multiline, R.id.nameTextView, choices);

And this is the content of "simple_dropdown_item_multiline":

<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
          android:layout_width="match_parent"
          android:layout_height="wrap_content">

<TextView android:id="@+id/nameTextView"
          style="?android:attr/dropDownItemStyle"
          xmlns:android="http://schemas.android.com/apk/res/android"
          android:layout_width="match_parent"
          android:layout_height="wrap_content"
          android:ellipsize="marquee"
          android:paddingBottom="@dimen/large"
          android:paddingTop="@dimen/large"
          android:singleLine="false"
          android:textAppearance="?android:attr/textAppearanceLargePopupMenu"/>

Solution 11 - Android

ArrayAdapter<?> specAdapter =
   ArrayAdapter.createFromResource(
       getActivity().getBaseContext(),
       aa[position],
       android.R.layout.select_dialog_item);
specAdapter.setDropDownViewResource(android.R.layout.select_dialog_item);

Solution 12 - Android

I had this same problem and found a solution.

I wanted the text to wrap in the initial display and in the dropdown view as well.

The text was wrapping in the initial display and for the dropdown I had found another solution recommending the use a custom view of a linear layout with fixed width enclosing a textview. This made the dropdown of the spinner look correct, as well as that of the initial selection. However, this caused a major problem on older devices.

The initial display would not refresh and the text took on a stacked look if I tried to select something else. and since it stacked downward adding a background didn't help. enter image description here

As it turns out adapters have a method called setDropDownViewResource() which allows you to set a different view for the dropdown than what is displayed in the spinner's initial selection.

 import org.holoeverywhere.widget.Spinner;

 ArrayAdapter adapter1 = ArrayAdapter.createFromResource(this,R.array.array_of_strings,R.layout.simple_list_item_1);
 adapter1.setDropDownViewResource(R.layout.my_simple_list_item_1);
 spQ1.setAdapter(adapter1);

in this example the simple_list_item is the default view supplied by android and my_simple_list_item is

<LinearLayout
xmlns:android="http://schemas.android.com/apk/res/android" 
android:layout_width="300dp"
android:layout_height="wrap_content" >   

<TextView
  android:id="@+id/android:text1"
  android:layout_width="wrap_content"
  android:layout_height="50dp"
  android:ellipsize="marquee"
  android:layout_gravity="center_vertical"
  android:singleLine="false"/>

</LinearLayout> 

Now the text wraps inside the dropdown view of the spinner AND in the spinners displayed selection.

Solution 13 - Android

Just wrap TextView with LinearLayout:

<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
              android:layout_width="match_parent"
              android:layout_height="wrap_content">
    <TextView
            android:id="@android:id/text1"
            android:layout_width="match_parent"
            android:layout_height="wrap_content"/>
</LinearLayout>

Solution 14 - Android

I resolved this issue by adding \n (new line) in the text (as simple as that).

<string name="bmr1">sedentary (little or no exercise)</string>
<string name="bmr2">lightly active (light exercise/\nsports 1-3 days/week)</string>
<string name="bmr3">moderately active (moderate exercise/\nsports 3-5 days/week)</string>
<string name="bmr4">very active (hard exercise/\nsports 6-7 days a week)</string>
<string name="bmr5">extra active (very hard exercise/sports &amp;\nphysical job or 2x training)</string>

Looks like this:

enter image description here

And this is the layout that the spinner uses as row:

<?xml version="1.0" encoding="utf-8"?>
<TextView xmlns:android="http://schemas.android.com/apk/res/android"
    android:id="@+id/default_listview_row"
    android:layout_width="match_parent"
    android:layout_height="wrap_content"
    android:singleLine="false"
    android:gravity="center_vertical"
    android:textColor="@android:color/white"
    android:background="@android:color/transparent"
    android:padding="5dp" />

Solution 15 - Android

    Spinner sp_type = dialogView.findViewById(R.id.sp_type);
    ArrayAdapter<String> dataAdapter = new ArrayAdapter<String>(getFragment().getContext(), android.R.layout.simple_spinner_dropdown_item, getFragment().getResources().getStringArray(R.array.courier_delivery_types_array));
dataAdapter.setDropDownViewResource(android.R.layout.simple_list_item_1);

simple_list_item_1 will display spinner dropdown textview with multiple lines. But spinner selected text shows in single line. We can replace 2nd line with simple_list_item_1 instead of simple_spinner_dropdown_item(For multiple lines)

Attributions

All content for this solution is sourced from the original question on Stackoverflow.

The content on this page is licensed under the Attribution-ShareAlike 4.0 International (CC BY-SA 4.0) license.

Content TypeOriginal AuthorOriginal Content on Stackoverflow
QuestionMatthias RobbersView Question on Stackoverflow
Solution 1 - AndroidKolchugaView Answer on Stackoverflow
Solution 2 - Androidandroid developerView Answer on Stackoverflow
Solution 3 - AndroidcVoroninView Answer on Stackoverflow
Solution 4 - AndroidMike SchallView Answer on Stackoverflow
Solution 5 - AndroidonosendaiView Answer on Stackoverflow
Solution 6 - AndroidgirlOnSledgeView Answer on Stackoverflow
Solution 7 - AndroidlokokoView Answer on Stackoverflow
Solution 8 - AndroidFajar RukmoView Answer on Stackoverflow
Solution 9 - AndroidJacoView Answer on Stackoverflow
Solution 10 - AndroidMarco C.View Answer on Stackoverflow
Solution 11 - Androiduser5723352View Answer on Stackoverflow
Solution 12 - AndroidMichael Alan HuffView Answer on Stackoverflow
Solution 13 - AndroidmixelView Answer on Stackoverflow
Solution 14 - AndroidGoran Horia MihailView Answer on Stackoverflow
Solution 15 - AndroidmalliView Answer on Stackoverflow