Фон элемента ListView с помощью настраиваемого селектора

98

Можно ли применить собственный фон к каждому элементу Listview через селектор списка?

Селектор по умолчанию указывает @android:color/transparentдля state_focused="false"случая, но изменение его на какой-либо настраиваемый вариант не влияет на элементы, которые не выбраны. В этом ответе Ромен Гай, кажется, предполагает, что это возможно.

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

Для справки, это селектор, который я использую, чтобы заставить его работать:

<selector xmlns:android="http://schemas.android.com/apk/res/android">

    <item android:state_focused="false"
        android:drawable="@drawable/list_item_gradient" />

    <!-- Even though these two point to the same resource, have two states so the drawable will invalidate itself when coming out of pressed state. -->
    <item android:state_focused="true" android:state_enabled="false"
        android:state_pressed="true"
        android:drawable="@drawable/list_selector_background_disabled" />
    <item android:state_focused="true" android:state_enabled="false"
        android:drawable="@drawable/list_selector_background_disabled" />

    <item android:state_focused="true" android:state_pressed="true"
        android:drawable="@drawable/list_selector_background_transition" />
    <item android:state_focused="false" android:state_pressed="true"
        android:drawable="@drawable/list_selector_background_transition" />

    <item android:state_focused="true"
        android:drawable="@drawable/list_selector_background_focus" />

</selector>

И вот как я устанавливаю селектор:

<ListView
    android:id="@android:id/list"
    android:layout_width="fill_parent"
    android:layout_height="fill_parent"
    android:listSelector="@drawable/list_selector_background" />    

Заранее благодарю за любую помощь!

шильгапира
источник

Ответы:

128

Я сам был разочарован этим и наконец решил это. Как намекнул Ромен Гай, есть еще одно состояние "android:state_selected", которое вы должны использовать. Используйте состояние, которое можно рисовать, для фона элемента списка и используйте другое состояние, которое можно рисовать для listSelectorвашего списка:

list_row_layout.xml:

<?xml version="1.0" encoding="utf-8"?>
<LinearLayout 
    xmlns:android="http://schemas.android.com/apk/res/android"
    android:layout_width="fill_parent"
    android:layout_height="?android:attr/listPreferredItemHeight"
    android:background="@drawable/listitem_background"
    >
...
</LinearLayout>

listitem_background.xml:

<?xml version="1.0" encoding="utf-8"?>
<selector xmlns:android="http://schemas.android.com/apk/res/android">
    <item android:state_selected="true" android:drawable="@color/android:transparent" />
    <item android:drawable="@drawable/listitem_normal" />
</selector>

layout.xml, который включает ListView:

...
<ListView 
   android:layout_width="fill_parent"
   android:layout_height="wrap_content"
   android:listSelector="@drawable/listitem_selector"
   />
...

listitem_selector.xml:

<?xml version="1.0" encoding="utf-8"?>
<selector xmlns:android="http://schemas.android.com/apk/res/android">
    <item android:state_pressed="true" android:drawable="@drawable/listitem_pressed" />
    <item android:state_focused="true" android:drawable="@drawable/listitem_selected" />
</selector>
dgmltn
источник
1
Спасибо за подробный ответ. Как я уже упоминал в своем вопросе, это именно то, чем я сейчас занимаюсь, и это работает довольно хорошо.
шильгапира
К сожалению, у меня все еще возникают проблемы с настройкой представления списка. Я разместил свои конкретные проблемы здесь: stackoverflow.com/questions/5426385/… ; Буду признателен за любой вклад, который вы можете внести.
LostNomad311
Это не работает, когда вы используете чертежи с 9 патчами с заполнением в качестве фона. Я нашел окончательное решение, думаю, посмотрите свой ответ
Michał K
Это работает для пост-ICS, но для Gingerbread весь список раскрашен нажатием или выбранной возможностью рисования.
gunar 09
Есть ли какое-нибудь решение для pre-ICS?
Lonli-Lokli
78

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

Теперь, если вы хотите иметь представление списка с разными состояниями и чертежами с 9 патчами (я думаю, он будет работать с любыми чертежами и цветами), вам нужно сделать 2 вещи:

  1. Установите селектор для элементов в списке.
  2. Избавьтесь от селектора по умолчанию для списка.

Что вам нужно сделать, это сначала установить row_selector.xml:

<?xml version="1.0" encoding="utf-8"?>
<selector xmlns:android="http://schemas.android.com/apk/res/android" >
    <item android:state_enabled="true" 
     android:state_pressed="true" android:drawable="@drawable/list_item_bg_pressed" />
    <item android:state_enabled="true"
     android:state_focused="true" android:drawable="@drawable/list_item_bg_focused" />
    <item android:state_enabled="true"
     android:state_selected="true" android:drawable="@drawable/list_item_bg_focused" />
    <item
     android:drawable="@drawable/list_item_bg_normal" />
</selector>

Не забывайте про android:state_selected. Он работает так же, как и android:state_focusedдля списка, но применяется к элементу списка.

Теперь примените селектор к элементам (row.xml):

<?xml version="1.0" encoding="utf-8"?>
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="fill_parent"
android:layout_height="wrap_content"
android:orientation="horizontal"
android:background="@drawable/row_selector"
>
...
</RelativeLayout>

Сделайте прозрачный селектор для списка:

<ListView
    android:id="@+id/android:list"
    ...
    android:listSelector="@android:color/transparent"
    />

Это должно сработать.

Михал К.
источник
1
+1, раньше не видел вашего ответа, наткнулся на такой же результат, делая самостоятельно. Но спасибо за упоминание о чертежах с 9 патчами, я уверен, что это может быть полезно в моих будущих реализациях. Это должен быть лучший ответ, потому что этот метод чище и удобнее в обслуживании.
IsaacCisneros
28
Мне очень жаль, но это такие вещи, которые иногда заставляют задуматься, почему эти чертовы интерфейсы API должны быть такими дрянными. Android еще многое предстоит сделать. Такие элементарные вещи не должны быть такими ПИТА.
Gubatron
2
+1 Спасибо, спасибо, спасибо. Прыгать через все эти обручи, чтобы достичь чего-то столь простого. Все эти состояния очень сбивают с толку.
Мориц
3
Вместо android: listSelector = "@ drawable / list_selector" просто напишите android: listSelector = "@ android: color / transparent". Нет необходимости в дополнительном list_selector.xml.
Sofi Software LLC,
1
Правильно, неразрушающий селектор списка может быть просто @android: color / transparent вместо вашего собственного селектора xml. Просто попробовал. То, что не работает, - это @ null, что меня удивляет, потому что в нем должно быть указано: «нет селектора списка», и поскольку селектор списка отображается позади элемента, ничего не должно отображаться. Но это не работает, селектор по умолчанию тогда ...
Зордид
17

Никогда не используйте "цвет фона" для строк вашего списка ...

это заблокирует каждое действие селектора (это была моя проблема!)

удачи!

cV2
источник
3
Это неправда. Просто используйте селектор в качестве фона, а не статичный цвет или рисование
Michał K
это должно было означать «цвет фона». полностью приму твои слова.
cV2
Я попал сюда, потому что теперь мне нужен селектор, который я использовал setBackgroundColorс «цветом». Вздох ... Используйте setBackgroundили setBackgroundDrawableсогласно вашей версии API с селектором.
EpicPandaForce
5

В статье «Почему мой список черный? Оптимизация под Android» в блоге разработчиков Android подробно объясняется, почему фон списка становится черным при прокрутке. Простой ответ: установите cacheColorHint в своем списке на прозрачный (# 00000000).

Скотт Д. Стрейдер
источник
1
Образцовая простота, но работает очень хорошо. Спасибо за Ромена Гая ... <ListView ... android: cacheColorHint = "# 00000000"> </ListView> Вот и все!
Эрик ДЖОЙЕ,
Не благодарите Ромена Гая. Он часть проблемы. В такой ОС не должно быть так много «скрытых уловок» или других хаков. Несмотря на то, что я люблю Android, это большая куча мусора по сравнению с другими SDK (даже более новыми / менее зрелыми!).
StackOverflowed
5

Достаточно, если вы вставите list_row_layout.xml:

<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="fill_parent"
android:layout_height="wrap_content"
android:gravity="center_vertical"
android:background="@drawable/listitem_background">... </LinearLayout>

listitem_selector.xml:

<?xml version="1.0" encoding="utf-8"?>
<selector xmlns:android="http://schemas.android.com/apk/res/android">
    <item android:drawable="@color/dark" android:state_pressed="true" />
    <item android:drawable="@color/dark" android:state_focused="true" />
    <item android:drawable="@android:color/white" />
</selector>
Кабесас
источник
2
потрясающе ... самый простой пример
Шихаб Уддин
1
А также сделать прозрачным селектор listview
Sayka 03 дек.15,
4

вместо того:

android:drawable="@color/transparent" 

записывать

android:drawable="@android:color/transparent"
Масло
источник
4

Вы можете написать тему:

<pre>

    android:name=".List10" android:theme="@style/Theme"

theme.xml

<style name="Theme" parent="android:Theme">
        <item name="android:listViewStyle">@style/MyListView</item>
</style>

styles.xml

 <style name="MyListView" parent="@android:style/Widget.ListView">
<item name="android:listSelector">@drawable/my_selector</item>

my_selector - это ваш пользовательский селектор. Извините, я не знаю, как писать свой код.

pengwang
источник
Спасибо за помощь, но это напрямую не помогает в установке фона списка по умолчанию с помощью селектора.
шильгапира 06
3

Я всегда использую один и тот же метод, и он работает каждый раз, везде: я просто использую такой селектор

<item android:state_activated="true"  android:color="@color/your_selected_color" />
<item android:state_pressed="true" android:color="@color/your_pressed_color" />
<item android:color="@color/your_normal_color"></item>

и установите в ListView (ЭТО ОЧЕНЬ ВАЖНО, ЧТОБЫ ЭТО РАБОТАЕТ) атрибут

android:choiceMode="singleChoice"

для textColor просто поместите в цветовую папку (ВАЖНО, не доступную папку!) селектор, подобный этому

<item android:state_activated="true"  android:color="@color/your_selected_textColor" />
<item android:state_pressed="true" android:color="@color/your_pressed_textColor" />
<item android:color="@color/your_normal_textColor"></item>

это образец шаблона строки

<ImageView
    android:id="@+skinMenu/lblIcon"
    android:layout_width="wrap_content"
    android:layout_height="wrap_content"
    android:gravity="center_horizontal"
    android:src="@drawable/menu_catalog" />

<TextView
    android:id="@+skinMenu/lblTitle"
    style="@style/superlabelStyle"
    android:layout_width="wrap_content"
    android:layout_height="match_parent"
    android:layout_marginLeft="20dp"
    android:gravity="center_vertical"
    android:text="test menu testo"
    android:textColor="@color/menu_textcolor_selector"
    android:textSize="20dp"
    android:textStyle="bold" />

все, что было показано, работает без утомительных обходных путей. надеюсь, что это поможет

Апперсайд
источник
2

Я не уверен, как добиться желаемого эффекта с помощью самого селектора - в конце концов, по определению, есть один селектор на весь список.

Однако вы можете управлять изменениями выделения и рисовать все, что хотите. В этом примере проекта я делаю селектор прозрачным и рисую полосу на выбранном элементе.

CommonsWare
источник
В самом деле, имеет смысл использовать не более одного селектора. Думаю, я (или был) немного смущен наличием <item android:state_focused="false" android:drawable="@android:color/transparent" />предложения в селекторе по умолчанию.
шильгапира 06
Марк, поигрался (с той же проблемой, что и Гил), я попробовал ваше решение, это только частично для трекбола, ничего не вызывается, когда вы касаетесь элементов, используя только касание, и onItemClicked запускается, если вы хотите что-то в этом роде, скорее всего вы должны отслеживать выбранные и нажатые позиции внутри ListView и рисовать то, что вам нужно, в самом элементе (сделайте его настраиваемым View) и в dispatchDraw ListView.
codeScriber
@codeScriber: «nothingSeelected вызывается, когда вы касаетесь элементов, используя только касание, и запускается onItemClicked» - это совершенно нормальное поведение, поскольку касание не имеет ничего общего с выделением.
CommonsWare
-3

Команда FrostWire здесь.

Вся API-интерфейс селектора работает не так, как ожидалось. Попробовав все решения, представленные в этой ветке, безрезультатно, мы просто решили проблему в момент раздувания элемента ListView.

  1. Убедитесь, что ваш элемент сохраняет свое состояние, мы сделали это как переменную-член MenuItem (выбрано логическое значение)

  2. Когда вы надуваете, спросите, выбран ли базовый элемент, если да, просто установите доступный ресурс, который вы хотите, в качестве фона (будь то 9patch или что-то еще). Убедитесь, что ваш адаптер знает об этом и вызывает notifyDataChanged (), когда что-то было выбрано.

        @Override
    public View getView(int position, View convertView, ViewGroup parent) {
        View rowView = convertView;
        if (rowView == null) {
            LayoutInflater inflater = act.getLayoutInflater();
            rowView = inflater.inflate(R.layout.slidemenu_listitem, null);
            MenuItemHolder viewHolder = new MenuItemHolder();
            viewHolder.label = (TextView) rowView.findViewById(R.id.slidemenu_item_label);
            viewHolder.icon = (ImageView) rowView.findViewById(R.id.slidemenu_item_icon);
            rowView.setTag(viewHolder);
        }
    
        MenuItemHolder holder = (MenuItemHolder) rowView.getTag();
        String s = items[position].label;
        holder.label.setText(s);
        holder.icon.setImageDrawable(items[position].icon);
    
        //Here comes the magic
        rowView.setSelected(items[position].selected);
    
        rowView.setBackgroundResource((rowView.isSelected()) ? R.drawable.slidemenu_item_background_selected : R.drawable.slidemenu_item_background);
    
        return rowView;
    }

Было бы здорово, если бы селекторы действительно работали, теоретически это красивое и элегантное решение, но похоже, что оно сломано. ПОЦЕЛУЙ.

Губатрон
источник
Я хочу попробовать это решение, не могли бы вы дать более подробное объяснение или полное решение? Спасибо
Эндрю Лам