Spinner不会包装文本 - 这是一个Android错误吗?

时间:2013-01-03 12:37:32

标签: android android-layout spinner android-spinner

如果Spinner项目的文本太长而无法放入一行,则文本不会被包装但会被截断。这 API级别> = 11 的情况。以下是Android 4.2.2 (左)的屏幕截图,其中显示了错误的行为,Android 2.3.3 (右)显示了预期效果。

android:singleLine="false"在这里被忽略了。所有其他尝试都像android:linesandroid:minLines等一样。TextView似乎比窗口宽度宽得多。

我看到其他人遇到同样的问题,但没有人能找到解决方案。那么,这是一个系统错误吗?我不认为操作系统版本之间的这种不一致是可能的。


请注意:

有一些答案表明相对简单的解决方案。

  • 撰写自定义Adapter并覆盖getView()以及getDropDownView()。这不是解决方案,因为在这一点上,仍然存在原始问题:布局如何处理正确的换行?

  • 将下拉视图的TextView包装到父ViewGroup中。不适用于android:layout_width="match_parent",因为父母的宽度似乎是无限的。

  • 为下拉视图提供固定宽度。这不适合Spinner可以具有的不同宽度。

  • 当然,没有办法解决方法是将\n手动插入文本中的任何位置。


使用以下代码重现:

更新:我还将其作为示例项目上传到GitHubDownload

/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" />

设置Adapter

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

15 个答案:

答案 0 :(得分:32)

在holo主题中,微调器默认使用下拉模式。所有具有覆盖默认样式的移动只是移动到将微调器模式切换到对话框模式,该模式成功地包装了api 11中的多行文本。相反,您可以使用new Spinner(context, Spinner.MODE_DIALOG)或xml:android:spinnerMode="dialog"创建微调器。但它并没有解决问题,因为它是对话框,而不是下拉列表。

我找到了另一种解决此问题的方法:在getDropDownView中覆盖ArrayAdapter方法,并将setSingleLine(false)放在post方法视图中。因此,当视图完全创建时,它将文本包装到适当的行。

@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;
}

<强>更新

这是另一个答案。

在PopupWindow中手动包装listview并在单击TextView下显示它,并在listItem单击上隐藏它。

简单实现只是为了表明想法:

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

答案 1 :(得分:13)

我已经通过切换到对话框式微调器解决了这个问题:

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

答案 2 :(得分:13)

这里只有一组解决方案适合我(在Android 5.1上测试过):

<强> 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>

<强>码

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

答案 3 :(得分:7)

在TextView周围添加LinearLayout可以正确包装文本。

布局(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>

适配器:

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;
    }

}

答案 4 :(得分:6)

使用Spinner主题时,在Holo上实现多行下拉项目是不可能的,这是我尝试过的。

解决方法是:

  • Spinner创建一个不从Holo继承的样式。这将启用多行下拉项。
  • 手动调整微调器的样式,使它看起来像是Holo - 主题。

这会产生(显示关闭和打开状态):

enter image description here

实施细节:

我无法继承Holo上的Spinner主题并在Spinner下拉列表中显示多行,即使我们设置了下拉项目的TextView singleLine属性为false,并提供自定义布局。我也试过保持Holo风格,但改变了

android:spinnerStyle
android:spinnerItemStyle 
android:spinnerDropDownItemStyle 

样式属性(使用这些属性的示例here)但我无法使其产生多行结果。

但是,如果我们覆盖Spinner的样式并且不从spinnerStyle继承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>

然后下拉项将支持显示多行。但现在我们丢失了Holo上的Spinner主题,而关闭状态看起来像TextView而不是Spinner没有箭头或视觉线索它是{{1} }。如果我们将Spinner父级设置为:spinnerStyle

parent="android:style/Widget.Spinner

<style name="spinnerStyle" parent="android:style/Widget.Spinner"> <item name="android:clickable">true</item> </style> 已关闭状态会显示箭头,但样式会像灰色前Holo Spinner一样,在Spinner应用中看起来不合适。

因此,可能的解决方案是:

  • 覆盖Holo的主题,不要将spinnerStyle用于父级。这将启用DropDown项目中的多行文字。
  • Holo背景更改为继承Spinner主题。

以下是一个例子:

创建基本活动:

Holo

活动布局:

@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));        
}

样式:

<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>

在drawable文件夹中,放置spinner_background_holo_light:

<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>

并在<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> 文件夹中包含这些drawable:

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

这会生成一个带有drawables-hdpi主题的微调器,主题为关闭状态和多行项目,如上面的屏幕截图所示。

此示例中的下拉项目不是Holo - 主题,但如果下拉项目的多行显示非常重要,则可能是可接受的权衡。

在此示例中,“{1}}在”清单“中设置为Holoandroid:minSdkVersion设置为14

android:targetSdkVersion图片和17代码来自HoloEverywhere版权所有(c)2012 Christophe Versieux,Sergey Shatunov。有关许可证详细信息,请参阅linked-to github项目。

答案 5 :(得分:3)

我认为android上有一个bug。你可以试试这个。从文本中删除空格,然后显示它将正常工作。如果textview的长度是&lt;字符串的那个,它忽略了空格后面的所有字符。 对于解决方法,你可以试试这个:

使用示例代码将文件添加到名为multiline_spinner_dropdown_item.xml的res / layout文件夹中:

<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" />

当您创建微调器时,从此布局创建它。

类似的东西:

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

基本上,将android.R.layout.simple_spinner_dropdown_item布局复制到项目中,并通过在CheckedTextView中将singleLine属性设置为false来修改布局。

答案 6 :(得分:3)

我遇到了同样的问题。我想在微调器的下拉列表中看到2行,但我发现的所有解决方案在我看来都不合理解决这个简单的问题。我调查了Spinner source code,我发现如果我们使用带有属性android的自定义.xml :singleLine =&#34; false&#34;

<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" />

默认 ArrayAdapter,每次都在ListPopupWindow执行代码

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

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

            return view;        
}

这就是我们为每个列表行只看到一个字符串行的原因,它实际上是在滚动。

要解决此类问题,我们的视图应该是TextView的 not instance ,只需将TextView放在FrameLayout或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>

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

这个解决方案适用于两个微调模式:MODE_DROPDOWN和MODE_DROPDOWN.Hope它可以帮助你!

答案 7 :(得分:1)

阅读这个答案:textview casting error:- android.widget.LinearLayout cannot be cast to android.widget.TextView和这个主题,我可以解决这个问题:我们需要一个LinearLayout包装TextView(微调文本)来避免文本从屏幕上退出,但是我们会遇到一些问题解决。首先,创建布局(我称之为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>

下一步是创建一个ArrayAdapter实例,将其设置为微调器:

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

不要忘记在ArrayAdapter上添加布局名称和TextView id,因为我们添加了LinearLayout,我们需要指定TextView,并覆盖getDropDownView以获取显示指定位置的数据的视图数据集。现在,我们可以看到微调器在较新和较旧的Android版本上运行良好

答案 8 :(得分:1)

我刚刚发现android已经存在这种情况的样式

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

希望它能解决你的问题。

答案 9 :(得分:0)

我遇到了同样的问题并找到了解决方案。

我希望文本包装在初始显示中以及下拉视图中。

文本正在包装在初始显示中,对于下拉列表,我找到了另一种解决方案,建议使用包含文本视图的固定宽度的线性布局的自定义视图。这使得微调器的下拉看起来正确,以及初始选择的下拉。但是,这在旧设备上造成了严重问题。

如果我尝试选择其他内容,初始显示将不会刷新并且文本呈现堆叠外观。而且由于它向下堆叠添加背景并没有帮助。 enter image description here

事实证明,适配器有一个名为setDropDownViewResource()的方法,它允许您为下拉菜单设置不同于微调器初始选择中显示的视图。

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

在这个例子中,simple_list_item是android提供的默认视图,my_simple_list_item是

<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> 

现在文本包含在微调器的下拉视图内并显示在微调器中。

答案 10 :(得分:0)

只需使用TextView

包裹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>

答案 11 :(得分:0)

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

答案 12 :(得分:0)

以下是我为使其发挥作用所做的一切:

add_action('init', 'custom_rewrite_basic');
function custom_rewrite_basic() {
    $rcat='(gmat|gre|sat|cat)';
    add_rewrite_tag( '%rootcat%',$rcat);
    add_rewrite_rule('^{$rcat}/resources/', 'index.php?    pagename=leap_resources&rootcat=$matches[1]', 'top');
}

这是“simple_dropdown_item_multiline”的内容:

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

答案 13 :(得分:0)

    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将显示带有多行的微调框下拉文本视图。但是微调框选择的文本以单行显示。我们可以将第二行替换为simple_list_item_1而不是simple_spinner_dropdown_item(用于多行)

答案 14 :(得分:-1)

我通过在文本中添加\ n(新行)解决了这个问题(就像那样简单)。

<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>

看起来像这样:

enter image description here

这是微调器用作行的布局:

<?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" />