Spinner does not wrap text -- is this an Android bug?
AndroidAndroid LayoutSpinnerAndroid SpinnerAndroid 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 overridinggetView()
as well asgetDropDownView()
. 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 parentViewGroup
. Does not work withandroid: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));
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 fromHolo
. 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):
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 useHolo
for parent. This will enable multi-line text in the DropDown items. - Change the
Spinner
background to look like it inherits theHolo
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:
spinner_default_holo_light.9.png
spinner_disabled_holo_light.9.png
spinner_focused_holo_light.9.png
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.
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 &\nphysical job or 2x training)</string>
Looks like this:
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)