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!

Shilgapira
la source

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>
dgmltn
la source
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.

Michał K
la source
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!

cV2
la source
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).

Scott D. Strader
la source
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>
Cabezas
la source
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"
Les beurres
la source
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

pengwang
la source
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é.

CommonsWare
la source
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

Apperside
la source
-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.

Gubatron
la source
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