Arrière-plan de l'élément ListView via le sélecteur personnalisé


98

Est-il possible d'appliquer un arrière-plan personnalisé à chaque élément Listview via le sélecteur de liste?

Le sélecteur par défaut spécifie @android:color/transparentle state_focused="false"cas, mais le fait de le remplacer par un dessin personnalisé n'affecte pas les éléments non sélectionnés. Romain Guy semble suggérer dans cette réponse que cela est possible.

J'obtiens actuellement le même effet en utilisant un arrière-plan personnalisé sur chaque vue et en le cachant lorsque l'élément est sélectionné / focalisé / quoi que ce soit pour que le sélecteur soit affiché, mais il serait plus élégant de tout définir au même endroit.

Pour référence, c'est le sélecteur que j'utilise pour essayer de faire fonctionner cela:

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

Et voici comment je règle le sélecteur:

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

Merci d'avance pour votre aide!

Réponses:


128

J'ai été frustré par cela moi-même et je l'ai finalement résolu. Comme l'a laissé entendre Romain Guy, il y a un autre état "android:state_selected", que vous devez utiliser. Utilisez un état pouvant être dessiné pour l'arrière-plan de votre élément de liste et utilisez un état différent pouvant être dessiné pour listSelectorvotre liste:

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 qui inclut le 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
Merci pour la réponse approfondie. Comme je l'ai mentionné dans ma question, c'est exactement ce que je fais en ce moment également et cela fonctionne très bien.
shilgapira

Malheureusement, je rencontre toujours des problèmes pour personnaliser ma vue de liste. J'ai posté mes problèmes spécifiques ici: stackoverflow.com/questions/5426385/… ; J'apprécierais toute contribution que vous pourriez avoir.
LostNomad311

Cela ne fonctionne pas lorsque vous utilisez des dessinables à 9 patchs avec un remplissage en arrière-plan. J'ai trouvé la solution ultime, je pense, voir ma réponse
Michał K

Cela fonctionne pour le post-ICS, mais pour Gingerbread, la liste entière est colorée avec le drawable pressé ou sélectionné.
gunar

Existe-t-il une solution pour le pré-ICS?
Lonli-Lokli

78

La solution de dglmtn ne fonctionne pas lorsque vous avez un 9 patch dessiné avec un remplissage en arrière-plan. Des choses étranges se produisent, je ne veux même pas en parler, si vous avez un tel problème, vous les connaissez.

Maintenant, si vous voulez avoir une liste avec différents états et 9 patchables (cela fonctionnerait avec tous les drawables et couleurs, je pense), vous devez faire 2 choses:

  1. Définissez le sélecteur pour les éléments de la liste.
  2. Débarrassez-vous du sélecteur par défaut de la liste.

Ce que vous devez faire est d'abord de définir le 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>

N'oubliez pas le android:state_selected. Cela fonctionne comme android:state_focusedpour la liste, mais il est appliqué pour l'élément de liste.

Appliquez maintenant le sélecteur aux éléments (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>

Créez un sélecteur transparent pour la liste:

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

Cela devrait faire la chose.


1
+1, je n'ai pas vu votre réponse avant, je suis tombé sur le même résultat en le faisant moi-même. Mais merci d'avoir mentionné les tirables à 9 patchs, je suis sûr que cela pourrait être utile dans mes futures implémentations. Cela devrait être la meilleure réponse car cette méthode est plus propre et maintenable.
IsaacCisneros

28
Je suis désolé, mais c'est le genre de choses qui font parfois réfléchir à la raison pour laquelle ces satanés API d'interface utilisateur doivent être si merdiques. Android a encore beaucoup à faire. Des choses aussi basiques ne devraient pas être une telle PITA.
Gubatron du

2
+1 Merci, merci, merci. Sauter à travers tous ces obstacles pour réaliser quelque chose d'aussi basique. Tous ces états sont tellement déroutants.
Moritz

3
Au lieu d'android: listSelector = "@ drawable / list_selector" écrivez simplement android: listSelector = "@ android: color / transparent". Pas besoin du list_selector.xml supplémentaire.
Sofi Software LLC

1
Correct, le sélecteur de liste non destructif peut simplement être @android: color / transparent au lieu de votre propre sélecteur XML. Juste essayé. La chose qui ne fonctionne pas est @ null, ce qui me surprend car cela devrait indiquer: "pas de sélecteur de liste" et comme le sélecteur de liste est dessiné derrière l'élément, rien ne doit être dessiné. Mais cela ne fonctionne pas, le sélecteur par défaut est à l'époque ...
Zordid

17

N'utilisez jamais de "couleur d'arrière-plan" pour vos lignes de vue de liste ...

cela bloquera chaque action du sélecteur (c'était mon problème!)

bonne chance!


3
Ce n'est pas vrai. Utilisez simplement un sélecteur comme arrière-plan, pas une couleur statique ou dessinable
Michał K

on entendait cela par "couleur de fond". acceptez complètement vos paroles.
cV2 du

Je suis arrivé ici parce que j'ai besoin d'un sélecteur maintenant que j'ai utilisé setBackgroundColoravec une "couleur". Soupir ... Utilisez setBackgroundou setBackgroundDrawableselon votre version d'API avec un sélecteur.
EpicPandaForce

5

L'article "Pourquoi ma liste est-elle noire? Une optimisation Android" dans le blog des développeurs Android explique en détail pourquoi l'arrière-plan de la liste devient noir lors du défilement. Réponse simple: définissez cacheColorHint sur votre liste sur transparent (# 00000000).


1
Une simplicité exemplaire mais ça marche très bien. Merci pour Romain Guy ... <ListView ... android: cacheColorHint = "# 00000000"> </ListView> C'est tout!
Eric JOYÉ

Ne remercie pas Romain Guy. Il fait partie du problème. Il ne devrait pas y avoir autant de "trucs cachés" ou autres hacks dans un système d'exploitation comme celui-ci. Même si j'aime Android, c'est un gros tas de dogcrap par rapport aux autres SDK (même les plus récents / moins matures!).
StackOverflowed

5

C'est suffisant, si vous mettez 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
génial ... exemple le plus simple
Shihab Uddin

1
Et aussi rendre le sélecteur de liste transparent
Sayka

4

au lieu de:

android:drawable="@color/transparent" 

écrire

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

4

Vous pouvez écrire un thème:

<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 est votre choix personnalisé pour le sélecteur Je suis désolé je ne sais pas comment écrire mon code


Merci pour l'aide, mais cela n'aide pas directement à définir l'arrière-plan de l'élément de liste par défaut via le sélecteur.
shilgapira

2

Je ne sais pas comment obtenir l'effet souhaité via le sélecteur lui-même - après tout, par définition, il existe un sélecteur pour toute la liste.

Cependant, vous pouvez contrôler les changements de sélection et dessiner ce que vous voulez. Dans cet exemple de projet , je rend le sélecteur transparent et dessine une barre sur l'élément sélectionné.


En effet, il est tout à fait logique qu'il n'y ait au plus qu'un seul sélecteur. Je suppose que je suis (ou étais) un peu confus par l'existence de la <item android:state_focused="false" android:drawable="@android:color/transparent" />clause dans le sélecteur par défaut.
shilgapira

Mark, en jouant (ayant le même problème que Gil) j'ai essayé votre solution, ce n'est que partiel pour la trackball uniquement, rienSeelected n'est appelé lorsque vous touchez des éléments en utilisant uniquement le toucher et onItemClicked est déclenché, si vous voulez quelque chose de ce genre, très probablement vous devez garder une trace des positions sélectionnées et pressées à l'intérieur de la ListView et dessiner ce dont vous avez besoin dans l'élément lui-même (le rendre personnalisé) et dans dispatchDraw de la ListView.
codeScriber

@codeScriber: "NothingSeelected est appelé lorsque vous touchez des éléments en utilisant uniquement le toucher et que onItemClicked est déclenché" - c'est un comportement parfaitement normal, puisque le toucher n'a rien à voir avec la sélection.
CommonsWare

2

J'utilise toujours la même méthode et ça marche à chaque fois, partout: j'utilise simplement un sélecteur comme celui-ci

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

et définissez sur ListView (CECI EST TRÈS IMPORTANT POUR LE FAIRE FONCTIONNER) l'attribut

android:choiceMode="singleChoice"

pour le textColor il suffit de mettre dans le dossier de couleur (IMPORTANT, pas de dossier dessinable!) un sélecteur comme celui-ci

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

ceci un exemple de modèle de ligne

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

tout fonctionne sans solutions fastidieuses. espère cette aide


-3

L'équipe FrostWire ici.

Toutes les API de merde de sélecteur ne fonctionnent pas comme prévu. Après avoir essayé toutes les solutions présentées dans ce fil de discussion, nous venons de résoudre le problème au moment de gonfler l'élément ListView.

  1. Assurez-vous que votre élément conserve son état, nous l'avons fait en tant que variable membre du MenuItem (booléen sélectionné)

  2. Lorsque vous gonflez, demandez si l'élément sous-jacent est sélectionné, si c'est le cas, définissez simplement la ressource dessinable que vous voulez comme arrière-plan (que ce soit un 9patch ou autre). Assurez-vous que votre adaptateur en est conscient et qu'il appelle notifyDataChanged () lorsqu'un élément a été sélectionné.

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

Ce serait vraiment bien si les sélecteurs fonctionnaient réellement, en théorie, c'est une solution agréable et élégante, mais il semble qu'elle soit cassée. BAISER.


Je veux essayer cette solution, pourriez-vous s'il vous plaît donner des explications plus détaillées ou une solution complète? Merci
Andrew Lam
En utilisant notre site, vous reconnaissez avoir lu et compris notre politique liée aux cookies et notre politique de confidentialité.
Licensed under cc by-sa 3.0 with attribution required.