Fondo del elemento ListView a través del selector personalizado

98

¿Es posible aplicar un fondo personalizado a cada elemento de Listview a través del selector de lista?

El selector predeterminado especifica @android:color/transparentel state_focused="false"caso, pero cambiarlo a algún elemento de diseño personalizado no afecta a los elementos que no están seleccionados. Romain Guy parece sugerir en esta respuesta que esto es posible.

Actualmente estoy logrando el mismo efecto al usar un fondo personalizado en cada vista y ocultarlo cuando el elemento está seleccionado / enfocado / lo que sea para que se muestre el selector, pero sería más elegante tener todo esto definido en un solo lugar.

Como referencia, este es el selector que estoy usando para intentar que esto funcione:

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

Y así es como estoy configurando el selector:

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

¡Gracias de antemano por cualquier ayuda!

shilgapira
fuente

Respuestas:

128

Yo mismo me he sentido frustrado y finalmente lo resolví. Como insinuó Romain Guy, hay otro estado "android:state_selected", que debes usar. Use un dibujable de estado para el fondo de su elemento de lista, y use un dibujable de estado diferente para listSelectorde su lista:

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 que incluye 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
fuente
1
Gracias por la respuesta en profundidad. Como mencioné en mi pregunta, esto es exactamente lo que estoy haciendo en este momento y funciona bastante bien.
shilgapira
Desafortunadamente, sigo teniendo problemas para personalizar mi vista de lista. Publiqué mis problemas específicos aquí: stackoverflow.com/questions/5426385/… ; Agradecería cualquier aporte que pueda tener.
LostNomad311
No funciona cuando usa elementos de diseño de 9 parches con relleno como fondo. Encontré la solución definitiva, creo, vea mi respuesta
Michał K
Esto funciona para post-ICS, pero para Gingerbread toda la lista está coloreada con dibujante seleccionado o presionado.
gunar
¿Existe alguna solución para los pre-ICS?
Lonli-Lokli
78

La solución de dglmtn no funciona cuando tiene un dibujable de 9 parches con relleno como fondo. Suceden cosas raras, ni siquiera quiero hablar de eso, si tienes un problema así, los conoces.

Ahora, si desea tener una vista de lista con diferentes estados y elementos de diseño de 9 parches (creo que funcionaría con cualquier elemento de diseño y color), debe hacer 2 cosas:

  1. Configure el selector para los elementos de la lista.
  2. Deshazte del selector predeterminado para la lista.

Lo que debe hacer primero es configurar 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>

No olvide el android:state_selected. Funciona como android:state_focusedpara la lista, pero se aplica para el elemento de la lista.

Ahora aplique el selector a los elementos (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>

Haz un selector transparente para la lista:

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

Esto debería hacer la cosa.

Michał K
fuente
1
+1, no vi tu respuesta antes, encontré el mismo resultado al hacerlo por mi cuenta. Pero gracias por mencionar los elementos de diseño de 9 parches, estoy seguro de que podrían ser útiles en mis futuras implementaciones. Esta debería ser la mejor respuesta porque este método es más limpio y fácil de mantener.
IsaacCisneros
28
Lo siento, pero este es el tipo de cosas que hacen pensar a veces por qué estas malditas API de IU tienen que ser tan malas. Android todavía tiene mucho por hacer. Cosas tan básicas no deberían ser un PITA.
Gubatron
2
+1 Gracias, gracias, gracias. Saltar a través de todos estos aros para lograr algo tan básico. Todos estos estados son muy confusos.
Moritz
3
En lugar de android: listSelector = "@ drawable / list_selector" simplemente escribe android: listSelector = "@ android: color / transparent". No es necesario el list_selector.xml adicional.
Sofi Software LLC
1
Correcto, el selector de lista no destructor puede ser simplemente @android: color / transparent en lugar de su propio selector xml. Sólo lo probé. Lo que no funciona es @ null, lo que me sorprende porque debería decir: "sin selector de lista" y como el selector de lista se dibuja detrás del elemento, no se debería dibujar nada. Pero eso no funciona, el selector predeterminado es en ese entonces ...
Zordid
17

Nunca use un "color de fondo" para sus filas de vista de lista ...

esto bloqueará cada acción del selector (¡era mi problema!)

¡buena suerte!

cV2
fuente
3
Esto no es verdad. Simplemente use un selector como fondo, no un color estático o dibujable
Michał K
esto tenía la intención de significar "color de fondo". acepta completamente tus palabras.
cV2
Llegué aquí porque necesito un selector ahora que lo he usado setBackgroundColorcon un "color". Suspiro ... Usa setBackgroundo setBackgroundDrawablesegún tu versión de API con un selector.
EpicPandaForce
5

El artículo "¿Por qué mi lista es negra? Una optimización de Android" en el Blog de desarrolladores de Android tiene una explicación detallada de por qué el fondo de la lista se vuelve negro cuando se desplaza. Respuesta simple: configure cacheColorHint en su lista en transparente (# 00000000).

Scott D. Strader
fuente
1
Una sencillez ejemplar pero que funciona muy bien. Gracias por Romain Guy ... <ListView ... android: cacheColorHint = "# 00000000"> </ListView> ¡Es todo!
Eric JOYÉ
No agradezcas a Romain Guy. El es parte del problema. No debería haber tantos "trucos ocultos" u otros trucos en un sistema operativo como este. Aunque me encanta Android, es una gran cantidad de basura en comparación con otros SDK (¡incluso los más nuevos / menos maduros!).
StackOverflowed
5

Es suficiente, si pones 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>
Cabezas
fuente
2
impresionante ... ejemplo más simple
Shihab Uddin
1
Y también haga que el selector de vista de lista sea transparente
Sayka
4

en vez de:

android:drawable="@color/transparent" 

escribir

android:drawable="@android:color/transparent"
Mantequillas
fuente
4

Puedes escribir un tema:

<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 es su selector de deseos personalizado Lo siento, no sé cómo escribir mi código

pengwang
fuente
Gracias por la ayuda, pero esto no ayuda directamente a configurar el fondo de listitem predeterminado a través del selector.
shilgapira
2

No estoy seguro de cómo lograr el efecto deseado a través del selector en sí; después de todo, por definición, hay un selector para toda la lista.

Sin embargo, puede controlar los cambios de selección y dibujar lo que quiera. En este proyecto de muestra , hago transparente el selector y dibujo una barra en el elemento seleccionado.

CommonsWare
fuente
De hecho, tiene mucho sentido que solo haya como máximo un selector. Supongo que estoy (o estaba) un poco confundido por la existencia de la <item android:state_focused="false" android:drawable="@android:color/transparent" />cláusula en el selector predeterminado.
shilgapira
Mark, jugando (tengo el mismo problema que Gil) probé tu solución, es solo parcial para trackball solamente, nadaSeleccionado se llama cuando tocas elementos usando solo toque y onItemClicked se activa, si quieres algo de ese tipo, lo más probable debe realizar un seguimiento de las posiciones seleccionadas y presionadas dentro de ListView y dibujar lo que necesita en el Elemento en sí (hacer que sea una Vista personalizada) y en dispatchDraw de ListView.
codeScriber
@codeScriber: "No se llama a nothingSeelected cuando tocas elementos con solo tocar y se activa onItemClicked" - este es un comportamiento perfectamente normal, ya que el tacto no tiene nada que ver con la selección.
CommonsWare
2

Siempre uso el mismo método y funciona siempre, en todas partes: simplemente uso un selector como este

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

y establezca en ListView (ESTO ES MUY IMPORTANTE PARA QUE FUNCIONE) el atributo

android:choiceMode="singleChoice"

para el textColor simplemente coloque en la carpeta de color (¡IMPORTANTE, carpeta no dibujable!)

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

esta es una plantilla de fila de muestra

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

todo funcionaba sin tediosas soluciones. espero que esto ayude

Apperside
fuente
-3

Equipo de FrostWire aquí.

Toda la API de basura del selector no funciona como se esperaba. Después de probar todas las soluciones presentadas en este hilo en vano, simplemente resolvimos el problema en el momento de inflar el elemento ListView.

  1. Asegúrese de que su artículo mantenga su estado, lo hicimos como una variable miembro de MenuItem (booleano seleccionado)

  2. Cuando infle, pregunte si el elemento subyacente está seleccionado, si es así, simplemente configure el recurso dibujable que desea como fondo (ya sea un parche 9 o lo que sea). Asegúrese de que su adaptador sea consciente de esto y de que llame a notifyDataChanged () cuando se haya seleccionado algo.

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

Sería realmente bueno si los selectores realmente funcionaran, en teoría es una solución agradable y elegante, pero parece que está rota. BESO.

Gubatron
fuente
Quiero probar esta solución, ¿podría dar una explicación más detallada o una solución completa? Gracias
Andrew Lam