Sfondo elemento ListView tramite selettore personalizzato


98

È possibile applicare uno sfondo personalizzato a ciascun elemento Listview tramite il selettore di elenco?

Il selettore predefinito specifica @android:color/transparentper il state_focused="false"caso, ma modificarlo in alcuni disegnabili personalizzati non influisce sugli elementi che non sono selezionati. Romain Guy sembra suggerire in questa risposta che questo è possibile.

Al momento sto ottenendo lo stesso effetto utilizzando uno sfondo personalizzato su ciascuna vista e nascondendolo quando l'elemento è selezionato / focalizzato / qualunque cosa in modo che venga mostrato il selettore, ma sarebbe più elegante averlo definito tutto in un unico posto.

Per riferimento, questo è il selettore che sto usando per provare a farlo funzionare:

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

Ed è così che imposto il selettore:

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

Grazie in anticipo per qualsiasi aiuto!

Risposte:


128

Sono stato frustrato da questo e alla fine l'ho risolto. Come ha accennato Romain Guy, c'è un altro stato "android:state_selected", che devi usare. Usa uno stato disegnabile per lo sfondo della tua voce di elenco e usa uno stato diverso per il listSelectortuo elenco:

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

1
Grazie per l'approfondita risposta. Come ho detto nella mia domanda, questo è esattamente quello che sto facendo anche in questo momento e funziona abbastanza bene.
shilgapira

Sfortunatamente, ho ancora problemi a personalizzare la visualizzazione elenco. Ho pubblicato i miei problemi specifici qui: stackoverflow.com/questions/5426385/… ; Apprezzerei qualsiasi input tu possa avere.
LostNomad311

Non funziona quando usi i drawable a 9 patch con imbottitura come sfondo. Ho trovato la soluzione definitiva, credo, vedi la mia risposta
Michał K

Questo funziona per il post-ICS, ma per Gingerbread l'intera lista è colorata con un disegnabile premuto o selezionato.
gunar

Esiste una soluzione per il pre-ICS?
Lonli-Lokli

78

La soluzione di dglmtn non funziona quando si dispone di un disegnabile a 9 patch con riempimento come sfondo. Succedono cose strane, non voglio nemmeno parlarne, se hai un problema del genere, le conosci.

Ora, se vuoi avere un listview con stati diversi e 9 patch disegnabili (funzionerebbe con qualsiasi drawable e colore, penso) devi fare 2 cose:

  1. Imposta il selettore per gli elementi nell'elenco.
  2. Elimina il selettore predefinito per l'elenco.

Quello che dovresti fare è prima impostare 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>

Non dimenticare il file android:state_selected. Funziona come android:state_focusedper l'elenco, ma viene applicato per l'elemento dell'elenco.

Ora applica il selettore agli elementi (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>

Crea un selettore trasparente per l'elenco:

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

Questo dovrebbe fare la cosa.


1
+1, non ho visto la tua risposta prima, mi sono imbattuto nello stesso risultato facendolo da solo. Ma grazie per aver menzionato i disegnabili a 9 patch, sono sicuro che potrebbe essere utile nelle mie future implementazioni. Questa dovrebbe essere la risposta migliore perché questo metodo è più pulito e manutenibile.
IsaacCisneros

28
Mi dispiace, ma questo è il tipo di cose che a volte fanno pensare al motivo per cui queste dannate API dell'interfaccia utente devono essere così schifose. Android ha ancora molto da affrontare. Queste cose basilari non dovrebbero essere una tale PITA.
Gubatron

2
+1 Grazie, grazie, grazie. Saltare attraverso tutti questi cerchi per ottenere qualcosa di così semplice. Tutti questi stati sono così confusi.
Moritz

3
Invece di android: listSelector = "@ drawable / list_selector" scrivi semplicemente android: listSelector = "@ android: color / transparent". Non c'è bisogno di list_selector.xml extra.
Sofi Software LLC

1
Corretto, il selettore dell'elenco non distruttivo può essere solo @android: color / transparent invece del tuo selettore xml. L'ho appena provato. La cosa che non funziona è @ null, che mi sorprende perché dovrebbe indicare: "nessun selettore di lista" e poiché il selettore di lista è disegnato dietro l'elemento, non dovrebbe essere disegnato nulla. Ma non funziona, il selettore predefinito è di nuovo allora ...
Zordid

17

Non usare mai un "colore di sfondo" per le righe della visualizzazione elenco ...

questo bloccherà ogni azione del selettore (era il mio problema!)

in bocca al lupo!


3
Questo non è vero. Usa un selettore come sfondo, non un colore statico o disegnabile
Michał K

questo doveva essere inteso con "colore di sfondo". accetta completamente le tue parole.
cV2

Sono arrivato qui perché ora ho bisogno di un selettore che ho usato setBackgroundColorcon un "colore". Sigh ... Usa setBackgroundo in setBackgroundDrawablebase alla tua versione API con un selettore.
EpicPandaForce

5

L'articolo "Perché il mio elenco è nero? Un'ottimizzazione per Android" nel Blog degli sviluppatori Android contiene una spiegazione approfondita del motivo per cui lo sfondo dell'elenco diventa nero durante lo scorrimento. Risposta semplice: imposta cacheColorHint nell'elenco su trasparente (# 00000000).


1
Una semplicità esemplare ma funziona molto bene. Grazie per Romain Guy ... <ListView ... android: cacheColorHint = "# 00000000"> </ListView> È tutto!
Eric JOYÉ

Non ringraziare Romain Guy. Fa parte del problema. Non dovrebbero esserci così tanti "trucchi nascosti" o altri hack in un sistema operativo come questo. Anche se adoro Android, è un mucchio di cazzate fumanti se confrontato con altri SDK (anche quelli più nuovi / meno maturi!).
StackOverflowed

5

È sufficiente, se inserisci 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
fantastico ... esempio più semplice
Shihab Uddin

1
Inoltre, rendi trasparente il selettore listview
Sayka

4

invece di:

android:drawable="@color/transparent" 

Scrivi

android:drawable="@android:color/transparent"

4

Puoi scrivere 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 è il tuo selettore personalizzato. Mi dispiace di non sapere come scrivere il mio codice


Grazie per l'aiuto, ma questo non aiuta direttamente nell'impostazione dello sfondo listitem predefinito tramite il selettore.
shilgapira

2

Non sono sicuro di come ottenere l'effetto desiderato tramite il selettore stesso - dopotutto, per definizione, c'è un selettore per l'intero elenco.

Tuttavia, puoi controllare le modifiche alla selezione e disegnare quello che vuoi. In questo progetto di esempio , rendo trasparente il selettore e disegno una barra sull'elemento selezionato.


In effetti, ha assolutamente senso che ci sia al massimo un selettore. Immagino di essere (o di essere stato) un po 'confuso dall'esistenza della <item android:state_focused="false" android:drawable="@android:color/transparent" />clausola nel selettore predefinito.
shilgapira

Mark, giocando (avendo lo stesso problema di Gil) ho provato la tua soluzione, è solo parziale solo per trackball, nothingSeelected viene chiamato quando tocchi oggetti usando solo touch e onItemClicked viene sparato, se vuoi qualcosa del genere, molto probabilmente devi tenere traccia delle posizioni selezionate e premute all'interno del ListView e disegnare ciò di cui hai bisogno nell'oggetto stesso (renderlo personalizzato View) e in dispatchDraw del ListView.
codeScriber

@codeScriber: "nothingSeelected viene chiamato quando tocchi gli elementi usando solo il tocco e onItemClicked viene attivato" - questo è un comportamento perfettamente normale, poiché il tocco non ha nulla a che fare con la selezione.
CommonsWare

2

Uso sempre lo stesso metodo e funziona sempre, ovunque: utilizzo semplicemente un selettore come questo

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

e impostare in ListView (QUESTO È MOLTO IMPORTANTE PER FARLO FUNZIONARE) l'attributo

android:choiceMode="singleChoice"

per il textColor basta mettere nella cartella dei colori (IMPORTANTE, cartella non disegnabile!) un selettore come questo

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

questo è un modello di riga di esempio

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

tutto ha dimostrato di funzionare senza noiose soluzioni alternative. spero che questo aiuto


-3

FrostWire Team qui.

Tutta la merda del selettore api non funziona come previsto. Dopo aver provato tutte le soluzioni presentate in questo thread per non andare bene, abbiamo appena risolto il problema al momento di gonfiare l'elemento ListView.

  1. Assicurati che il tuo elemento mantenga il suo stato, l'abbiamo fatto come variabile membro di MenuItem (booleano selezionato)

  2. Quando gonfi, chiedi se l'elemento sottostante è selezionato, in tal caso, imposta la risorsa disegnabile che desideri come sfondo (sia essa una patch 9 o qualsiasi altra cosa). Assicurati che il tuo adattatore sia a conoscenza di ciò e che richiami notifyDataChanged () quando qualcosa è stato selezionato.

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

Sarebbe davvero bello se i selettori funzionassero davvero, in teoria è una soluzione carina ed elegante, ma sembra che sia rotta. BACIO.


Voglio provare questa soluzione, potresti fornire una spiegazione più dettagliata o una soluzione completa? Grazie
Andrew Lam
Utilizzando il nostro sito, riconosci di aver letto e compreso le nostre Informativa sui cookie e Informativa sulla privacy.
Licensed under cc by-sa 3.0 with attribution required.