Spinner не переносит текст - это ошибка Android?

Если текст Spinner Элемент слишком длинный, чтобы поместиться в одну строку, текст не оборачивается, а обрезается. Это относится только к уровню API> = 11. Вот скриншоты Android 4.2.2 (слева), который показывает неправильное поведение, и Android 2.3.3 (справа), где он выглядит, как и ожидалось.

android:singleLine="false" просто игнорируется здесь. Так как все другие попытки, как android:lines, android:minLinesи т. д. TextView как-то кажется намного шире, чем ширина окна.

Я видел других людей, имеющих такую ​​же проблему, но никто не мог найти решение. Итак, это системная ошибка? Я не думаю, что это несоответствие между версиями ОС может быть намеренным.


Пожалуйста, обратите внимание:

Были некоторые ответы, предлагающие относительно простые решения.

  • Написание кастома Adapter и переопределение getView() так же как getDropDownView(), Здесь это не решение, потому что на данный момент все еще существует первоначальная проблема: как должен выглядеть макет для правильного переноса строк?

  • Упаковка TextView раскрывающегося представления в родительском ViewGroup, Не работает с android:layout_width="match_parent" потому что ширина родителя, как ни странно, кажется неограниченной.

  • Придание выпадающему виду фиксированной ширины. Это не подходит с различной шириной Spinner могу иметь.

  • И, конечно же, нет решения вручную вставить \nгде-нибудь в тексте.


Воспроизведите с помощью следующего кода:

ОБНОВЛЕНИЕ: Я также загрузил это как пример проекта на GitHub: Скачать

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

14 ответов

Решение

В голо-теме спиннер по умолчанию использует выпадающий режим. И все движения с переопределением стилей по умолчанию просто переходят к переключению режима счетчика в режим диалога, который успешно переносит многострочный текст, как в API 11. Вместо этого вы можете создать счетчик с new Spinner(context, Spinner.MODE_DIALOG) или в xml: android:spinnerMode="dialog", Но это не решает проблему, потому что это диалог, а не выпадающий список.

Я нашел другое решение этой проблемы: переопределить getDropDownView метод в ArrayAdapter и положи setSingleLine(false) в посте метод зрения. Поэтому, когда представление полностью создано, оно переносит текст в соответствующие строки.

@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 и покажите его в 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);
            }
        });
    }
}

У меня сработала только комбинация решений (проверено и на 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);

Я решил эту проблему, переключившись на спиннер в диалоговом стиле:

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

Добавление LinearLayout вокруг TextView позволяет правильно переносить текст.

Макет (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;
    }

}

Достижение многострочных выпадающих пунктов на Spinner при использовании Holo тема не возможна, из того что я пробовал.

Обходное решение заключается в следующем:

  • создать стиль для Spinner это не наследуется от Holo, Это включит многострочные выпадающие элементы.
  • Стиль Spinner "вручную", чтобы он выглядел так, как будто Holo -themed.

Это производит (показывая закрытые и открытые состояния):

Детали реализации:

Нет способа унаследовать от Holo тема на Spinner и показать несколько строк в Spinner насколько я могу судить, выпадающий элемент, даже если мы установим выпадающий элемент TextViewsingleLine приписывать false и предоставить нестандартный макет. Я также пытался сохранить Holo стиль, но изменяя

android:spinnerStyle
android:spinnerItemStyle 
android:spinnerDropDownItemStyle 

Атрибуты стилей (пример использования этих атрибутов здесь), но я не мог заставить его производить многострочный результат.

Тем не менее, если мы переопределим стиль для 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 без стрелки или визуальной подсказки, это Spinner, Если мы вместо этого установим spinnerStyle родитель для: parent="android:style/Widget.Spinner:

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

Spinner закрытое состояние покажет стрелку, но будет оформлено как серый pre-Holo Spinner который выглядит неуместным в Holo приложение.

Итак, возможное решение тогда:

  • Переопределить тему для spinnerStyle и не используйте Holo для родителей. Это включит многострочный текст в элементах DropDown.
  • Изменить 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>

стили:

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

в папке drawable поместите 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>

и включите эти Drawables в вашем drawables-hdpi папка:

spinner_default_holo_light.9.png

spinner_disabled_holo_light.9.png

spinner_focused_holo_light.9.png

spinner_pressed_holo_light.9.png

Это производит счетчик с Holo тематические закрытое состояние и многострочные элементы, как показано на скриншотах выше.

Выпадающие элементы в этом примере не являются Holo Тематически, но, возможно, это приемлемый компромисс, если многострочное отображение выпадающих элементов действительно важно.

В этом примере android:minSdkVersion был установлен на 14 а также android:targetSdkVersion в 17 в манифесте.

Holo графика и spinner_background_holo_light.xml код от HoloEverywhere Copyright (c) 2012 Кристоф Версье, Сергей Шатунов. См. Связанный с проектом github для деталей лицензии.

Я столкнулся с той же проблемой. Я хочу видеть 2 строки в выпадающем списке счетчика, но все решения, которые я нашел, кажутся мне неразумными для решения такой простой проблемы. Я исследую исходный код Spinner и обнаружил, что если мы используем пользовательский.xml с атрибутом 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" />

и 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, просто поместите ваше 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. Надеюсь, оно вам поможет!

Я думаю, что есть ошибка на Android. Вы можете попробовать это. Удалите пробелы из текста, а затем отобразите его будет работать нормально. Если длина текстового представления<длины строки, он игнорирует все символы после пробела. Для обходного пути вы можете попробовать это:

добавьте файл в папку res / layout с именем multiline_spinner_dropdown_item.xml с примером кода:

<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 в проект и измените макет, установив для атрибута singleLine значение false в CheckedTextView.

Я только что обнаружил, что 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);

надеюсь, это решило вашу проблему.

Вот что я сделал, чтобы это работало:

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

И это содержание "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"/>

Прочитав этот ответ: ошибка приведения в текстовое представление:- android.widget.LinearLayout не может быть приведен к 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);

Не забудьте добавить имя макета и идентификатор TextView в ArrayAdapter, потому что мы добавили LinearLayout, и нам нужно указать TextView и переопределить getDropDownView, чтобы получить представление, отображающее данные в указанной позиции в наборе данных. Теперь мы можем видеть, что спиннер работает хорошо на новых и старых версиях Android

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

У меня была такая же проблема и я нашел решение.

Я хотел, чтобы текст был перенесен на начальный экран, а также в раскрывающийся список.

Текст был перенесен на начальный экран, и для раскрывающегося списка я нашел другое решение, рекомендующее использовать пользовательский вид линейного макета с фиксированной шириной, охватывающий текстовое представление. Это сделало правильное выпадающее меню счетчика, так же как и при первоначальном выборе. Однако это вызвало серьезную проблему на старых устройствах.

Начальный экран не обновлялся, и текст приобретал сложенный вид, если я пытался выбрать что-то еще. и так как он сложен вниз, добавление фона не помогло.введите описание изображения здесь

Как выяснилось, у адаптеров есть метод 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> 

Теперь текст переносится внутри выпадающего списка счетчика И в отображаемом выделении счетчиков.

    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(для нескольких строк)

Просто заверните 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>

Я решил эту проблему, добавив \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>

Выглядит так:

И это макет, который вращатель использует в качестве строки:

<?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" />
Другие вопросы по тегам