ListView-Elementhintergrund über benutzerdefinierte Auswahl

98

Ist es möglich, über die Listenauswahl einen benutzerdefinierten Hintergrund auf jedes Listview-Element anzuwenden?

Die Standardauswahl gibt @android:color/transparentden state_focused="false"Fall an, aber das Ändern dieser Auswahl in eine benutzerdefinierte Zeichnungsfunktion wirkt sich nicht auf Elemente aus, die nicht ausgewählt sind. Romain Guy scheint in dieser Antwort darauf hinzuweisen, dass dies möglich ist.

Ich erreiche derzeit den gleichen Effekt, indem ich für jede Ansicht einen benutzerdefinierten Hintergrund verwende und ihn ausblende, wenn das Element ausgewählt / fokussiert / was auch immer angezeigt wird, aber es wäre eleganter, dies alles an einem Ort zu definieren.

Als Referenz ist dies der Selektor, mit dem ich versuche, dies zum Laufen zu bringen:

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

Und so stelle ich den Selektor ein:

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

Vielen Dank im Voraus für jede Hilfe!

Shilgapira
quelle

Antworten:

128

Ich war selbst frustriert und habe es endlich gelöst. Wie Romain Guy angedeutet hat, gibt es einen anderen Zustand, den "android:state_selected"Sie verwenden müssen. Verwenden Sie einen Status, der für den Hintergrund Ihres Listenelements gezeichnet werden kann, und einen anderen Status, der für listSelectorIhre Liste gezeichnet werden kann:

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 mit der 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
quelle
1
Vielen Dank für die ausführliche Antwort. Wie ich in meiner Frage erwähnt habe, mache ich genau das auch im Moment und es funktioniert ganz gut.
Shilgapira
Leider habe ich immer noch Probleme beim Anpassen meiner Listenansicht. Ich habe meine spezifischen Probleme hier veröffentlicht: stackoverflow.com/questions/5426385/… ; Ich würde mich über jede Eingabe freuen, die Sie haben könnten.
LostNomad311
Es funktioniert nicht, wenn Sie 9-Patch-Drawables mit Polsterung als Hintergrund verwenden. Ich denke, ich habe die ultimative Lösung gefunden, siehe meine Antwort
Michał K
Dies funktioniert für Post-ICS, aber für Lebkuchen wird die gesamte Liste mit gedrückt oder ausgewählt gezeichnet.
Gunar
Gibt es eine Lösung für Pre-ICS?
Lonli-Lokli
78

Die Lösung von dglmtn funktioniert nicht, wenn Sie ein 9-Patch-Zeichenelement mit Polsterung als Hintergrund haben. Seltsame Dinge passieren, ich möchte nicht einmal darüber sprechen, wenn Sie ein solches Problem haben, kennen Sie sie.

Wenn Sie eine Listenansicht mit verschiedenen Status und 9-Patch-Drawables haben möchten (ich denke, es würde mit allen Drawables und Farben funktionieren), müssen Sie zwei Dinge tun:

  1. Stellen Sie den Selektor für die Elemente in der Liste ein.
  2. Entfernen Sie die Standardauswahl für die Liste.

Was Sie tun sollten, ist zuerst die row_selector.xml zu setzen:

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

Vergiss das nicht android:state_selected. Es funktioniert wie android:state_focusedfür die Liste, wird jedoch für das Listenelement angewendet.

Wenden Sie nun den Selektor auf die Elemente an (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>

Machen Sie eine transparente Auswahl für die Liste:

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

Das sollte das Ding machen.

Michał K.
quelle
1
+1, ich habe deine Antwort vorher nicht gesehen, ich bin auf das gleiche Ergebnis gestoßen, als ich es alleine gemacht habe. Aber danke, dass Sie die 9-Patch-Drawables erwähnt haben, ich bin sicher, dass sie in meinen zukünftigen Implementierungen nützlich sein könnten. Dies sollte die beste Antwort sein, da diese Methode sauberer und wartbarer ist.
IsaacCisneros
27
Es tut mir leid, aber das ist die Art von Dingen, die manchmal zum Nachdenken bringen, warum diese verdammten UI-Apis so beschissen sein müssen. Android hat noch viel zu tun. Solche grundlegenden Dinge sollten keine solche PITA sein.
Gubatron
2
+1 Danke, danke, danke. Durch all diese Reifen springen, um etwas so Grundlegendes zu erreichen. Alle diese Zustände sind sehr verwirrend.
Moritz
3
Anstelle von android: listSelector = "@ drawable / list_selector" schreibe einfach android: listSelector = "@ android: color / transparent". Die zusätzliche list_selector.xml ist nicht erforderlich.
Sofi Software LLC
1
Richtig, der nicht zerstörende Listen-Selektor kann einfach @android sein: Farbe / transparent anstelle Ihres eigenen XML-Selektors. Ich habe es gerade versucht. Das, was nicht funktioniert, ist @ null, was mich überrascht, denn das sollte lauten: "kein Listenwähler" und da der Listenwähler hinter dem Element gezeichnet wird, sollte nichts gezeichnet werden. Aber das funktioniert nicht, der Standard-Selektor ist damals ...
Zordid
17

Verwenden Sie niemals eine "Hintergrundfarbe" für Ihre Listenansichtszeilen ...

Dies blockiert jede Auswahlaktion (war mein Problem!)

Viel Glück!

cV2
quelle
3
Das ist nicht wahr. Verwenden Sie einfach einen Selektor als Hintergrund, keine statische Farbe oder zeichnbar
Michał K
dies sollte mit "Hintergrundfarbe" gemeint sein. Akzeptiere deine Worte vollständig.
cV2
Ich bin hierher gekommen, weil ich jetzt einen Selektor brauche, den ich setBackgroundColormit einer "Farbe" verwendet habe. Seufz ... Verwenden Sie setBackgroundoder setBackgroundDrawableentsprechend Ihrer API-Version mit einem Selektor.
EpicPandaForce
5

Der Artikel "Warum ist meine Liste schwarz? Eine Android-Optimierung" im Android Developers Blog enthält eine ausführliche Erklärung, warum der Listenhintergrund beim Scrollen schwarz wird. Einfache Antwort: Setzen Sie cacheColorHint in Ihrer Liste auf transparent (# 00000000).

Scott D. Strader
quelle
1
Eine beispielhafte Einfachheit, die aber sehr gut funktioniert. Danke für Romain Guy ... <ListView ... android: cacheColorHint = "# 00000000"> </ ListView> Es ist alles!
Eric JOYÉ
Danke Romain Guy nicht. Er ist Teil des Problems. In einem solchen Betriebssystem sollte es nicht so viele "versteckte Tricks" oder andere Hacks geben. Obwohl ich Android liebe, ist es im Vergleich zu anderen SDKs (sogar neueren / weniger ausgereiften!) Ein großer dampfender Haufen Hundekot.
StackOverflowed
5

Es ist genug, wenn Sie Folgendes eingeben 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
quelle
2
genial ... einfachstes Beispiel
Shihab Uddin
1
Und machen Sie auch den Listenansicht-Selektor transparent
Sayka
4

anstatt:

android:drawable="@color/transparent" 

schreiben

android:drawable="@android:color/transparent"
Butter
quelle
4

Sie können ein Thema schreiben:

<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 ist Ihr Wunsch, einen benutzerdefinierten Selektor zu erstellen. Es tut mir leid, dass ich nicht weiß, wie ich meinen Code schreiben soll

Pengwang
quelle
Vielen Dank für die Hilfe, aber dies hilft nicht direkt beim Festlegen des Standardhintergrunds für Listenelemente über die Auswahl.
Shilgapira
2

Ich bin mir nicht sicher, wie Sie den gewünschten Effekt über den Selektor selbst erzielen können - schließlich gibt es per Definition einen Selektor für die gesamte Liste.

Sie können jedoch die Auswahländerungen steuern und zeichnen, was Sie möchten. In diesem Beispielprojekt mache ich den Selektor transparent und zeichne einen Balken auf das ausgewählte Element.

CommonsWare
quelle
In der Tat ist es absolut sinnvoll, dass es höchstens einen Selektor gibt. Ich glaube, ich bin (oder war) ein bisschen verwirrt von der Existenz der <item android:state_focused="false" android:drawable="@android:color/transparent" />Klausel im Standard-Selektor.
Shilgapira
Mark, ich habe herumgespielt (mit dem gleichen Problem wie Gil). Ich habe deine Lösung ausprobiert. Sie ist nur teilweise für den Trackball. NothingSeelected wird aufgerufen, wenn du Gegenstände nur mit Berührung berührst. OnItemClicked wird ausgelöst, wenn du etwas in dieser Art willst Sie müssen ausgewählte und gedrückte Positionen in der ListView verfolgen und das, was Sie benötigen, im Element selbst (benutzerdefinierte Ansicht erstellen) und in dispatchDraw der ListView zeichnen.
CodeScriber
@codeScriber: "nothingSeelected wird aufgerufen, wenn Sie Elemente nur mit touch berühren und onItemClicked ausgelöst wird" - dies ist ein völlig normales Verhalten, da touch nichts mit Auswahl zu tun hat.
CommonsWare
2

Ich benutze immer die gleiche Methode und sie funktioniert jedes Mal und überall: Ich benutze einfach einen Selektor wie diesen

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

und setzen Sie in der ListView (DAS IST SEHR WICHTIG, damit es funktioniert) das Attribut

android:choiceMode="singleChoice"

für die textColor legen Sie einfach in den Farbordner (WICHTIG, nicht zeichnbarer Ordner!) eine Auswahl wie diese

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

Dies ist eine Beispielzeilenvorlage

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

Alles funktioniert ohne langwierige Problemumgehungen. Ich hoffe das hilft

Apperside
quelle
-3

FrostWire Team hier.

Alle Selector Crap API funktioniert nicht wie erwartet. Nachdem wir alle in diesem Thread vorgestellten Lösungen als nicht gut ausprobiert haben, haben wir das Problem gerade im Moment des Aufblasens des ListView-Elements gelöst.

  1. Stellen Sie sicher, dass Ihr Artikel seinen Status beibehält. Wir haben ihn als Mitgliedsvariable des MenuItem erstellt (Boolescher Wert ausgewählt).

  2. Fragen Sie beim Aufblasen, ob das zugrunde liegende Element ausgewählt ist. Wenn ja, legen Sie einfach die gewünschte zeichnbare Ressource als Hintergrund fest (sei es ein 9-Patch oder was auch immer). Stellen Sie sicher, dass Ihr Adapter dies weiß und notifyDataChanged () aufruft, wenn etwas ausgewählt wurde.

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

Es wäre wirklich schön, wenn die Selektoren tatsächlich funktionieren würden. Theoretisch ist es eine schöne und elegante Lösung, aber es scheint, als wäre sie kaputt. KUSS.

Gubatron
quelle
Ich möchte diese Lösung ausprobieren. Könnten Sie bitte eine ausführlichere Erklärung oder eine vollständige Lösung geben? Vielen Dank
Andrew Lam