Tło elementu ListView za pomocą selektora niestandardowego

98

Czy za pomocą selektora listy można zastosować niestandardowe tło do każdego elementu widoku listy?

W selektor określa domyślny @android:color/transparentdla state_focused="false"sprawy, ale zmiana ta w jakimś niestandardowym rozciągliwej nie wpływa na elementy, które nie zostały wybrane. Romain Guy wydaje się sugerować w tej odpowiedzi, że jest to możliwe.

Obecnie osiągam ten sam efekt, używając niestandardowego tła w każdym widoku i ukrywając je, gdy element jest zaznaczony / wyostrzony / cokolwiek, aby selektor był pokazany, ale byłoby bardziej elegancko, gdyby to wszystko było zdefiniowane w jednym miejscu.

Dla porównania, oto selektor, którego używam, aby spróbować uruchomić to:

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

A tak ustawiam selektor:

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

Z góry dziękuję za pomoc!

shilgapira
źródło

Odpowiedzi:

128

Sam byłem tym sfrustrowany i ostatecznie go rozwiązałem. Jak zasugerował Romain Guy, istnieje inny stan, z "android:state_selected"którego musisz skorzystać. Użyj stanu, który można narysować jako tło elementu listy, i użyj innego stanu, który można narysować dla listSelectorswojej listy:

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, który zawiera 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
źródło
1
Dzięki za szczegółową odpowiedź. Jak wspomniałem w swoim pytaniu, właśnie to robię w tej chwili i działa całkiem dobrze.
shilgapira
Niestety nadal mam problemy z dostosowywaniem widoku listy. Opublikowałem tutaj moje konkretne problemy: stackoverflow.com/questions/5426385/… ; Byłbym wdzięczny za wszelkie uwagi.
LostNomad311
Nie działa, gdy używasz 9-łatek do rysowania z wypełnieniem jako tłem. Znalazłem ostateczne rozwiązanie, myślę, że zobacz moją odpowiedź
Michał K
Działa to w przypadku post-ICS, ale w przypadku Gingerbread cała lista jest pokolorowana za pomocą tłoczonych lub wybranych rysunków.
gunar
Czy jest jakieś rozwiązanie dla pre-ICS?
Lonli-Lokli
78

Rozwiązanie dglmtn nie działa, gdy masz 9 łatek do rysowania z wypełnieniem jako tłem. Dziwne rzeczy się zdarzają, nawet nie chcę o tym rozmawiać, jak masz taki problem to je znasz.

Teraz, jeśli chcesz mieć widok listy z różnymi stanami i elementami do rysowania z 9 łatkami (myślę, że działałby z dowolnymi rysunkami i kolorami), musisz zrobić 2 rzeczy:

  1. Ustaw selektor dla elementów na liście.
  2. Pozbądź się domyślnego selektora listy.

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

Nie zapomnij o android:state_selected. Działa jak android:state_focusedw przypadku listy, ale jest stosowany do elementu listy.

Teraz zastosuj selektor do elementów (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>

Utwórz przezroczysty selektor dla listy:

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

To powinno wystarczyć.

Michał K.
źródło
1
+1, nie widziałem wcześniej Twojej odpowiedzi, natknąłem się na ten sam wynik, robiąc to samodzielnie. Ale dziękuję za wspomnienie o rysunkach z 9 łatkami, jestem pewien, że mogą się przydać w moich przyszłych implementacjach. To powinna być najlepsza odpowiedź, ponieważ ta metoda jest czystsza i łatwa w utrzymaniu.
IsaacCisneros
28
Przykro mi, ale to są rzeczy, które czasami sprawiają, że myślę, dlaczego te cholerne interfejsy API muszą być takie gówniane. Android wciąż ma wiele do przejścia. Takie podstawowe rzeczy nie powinny być takie PITA.
Gubatron,
2
+1 Dziękuję, dziękuję, dziękuję. Przeskakiwanie przez wszystkie te obręcze, aby osiągnąć coś tak podstawowego. Wszystkie te stany są tak bardzo zagmatwane.
Moritz
3
Zamiast android: listSelector = "@ drawable / list_selector" po prostu napisz android: listSelector = "@ android: color / transparent". Nie ma potrzeby stosowania dodatkowego list_selector.xml.
Sofi Software LLC
1
Prawidłowo, nieniszczący selektor listy może być po prostu @android: color / transparent zamiast własnego selektora XML. Po prostu spróbowałem. Rzecz, która nie działa, to @ null, co mnie zaskakuje, ponieważ powinno to brzmieć: "brak selektora listy", a ponieważ selektor listy jest rysowany za elementem, nic nie powinno być rysowane. Ale to nie działa, domyślny selektor jest wtedy ...
Zordid
17

Nigdy, przenigdy nie używaj „koloru tła” dla wierszy widoku listy ...

to zablokuje każdą akcję selektora (to był mój problem!)

powodzenia!

cV2
źródło
3
To nie jest prawda. Po prostu użyj selektora jako tła, a nie statycznego koloru lub rysowania
Michał K
w zamierzeniu miało to oznaczać „kolor tła”. całkowicie zaakceptuj swoje słowa.
cV2,
Dotarłem tutaj, ponieważ teraz potrzebuję selektora, którego użyłem setBackgroundColorz „kolorem”. Ech ... Użyj setBackgroundlub setBackgroundDrawablezgodnie z wersją API z selektorem.
EpicPandaForce
5

Artykuł „Dlaczego moja lista jest czarna? Optymalizacja systemu Android” na blogu dla programistów Androida zawiera dokładne wyjaśnienie, dlaczego podczas przewijania tło listy zmienia kolor na czarny. Prosta odpowiedź: ustaw cacheColorHint na liście jako przezroczystą (# 00000000).

Scott D. Strader
źródło
1
Wzorowa prostota, ale działa bardzo dobrze. Dzięki za Romain Guy ... <ListView ... android: cacheColorHint = "# 00000000"> </ListView> To wszystko!
Eric JOYÉ
Nie dziękuj Romainowi Guyowi. On jest częścią problemu. W takim systemie operacyjnym nie powinno być tak wielu „ukrytych sztuczek” ani innych hacków. Mimo że uwielbiam Androida, w porównaniu z innymi pakietami SDK (nawet nowszymi / mniej dojrzałymi!) Jest to spora kupa bzdur.
StackOverflowed
5

Wystarczy, jeśli włożysz 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
źródło
2
niesamowity ... najprostszy przykład
Shihab Uddin
1
A także uczyń selektor widoku listy przezroczystym
Sayka
4

zamiast:

android:drawable="@color/transparent" 

pisać

android:drawable="@android:color/transparent"
Masła
źródło
4

Możesz napisać motyw:

<pre>

    android:name=".List10" android:theme="@style/Theme"

theme.xml

<style name="Theme" parent="android:Theme">
        <item name="android:listViewStyle">@style/MyListView</item>
</style>

style.xml

 <style name="MyListView" parent="@android:style/Widget.ListView">
<item name="android:listSelector">@drawable/my_selector</item>

my_selector to Twój niestandardowy selektor. Przykro mi, że nie wiem, jak napisać mój kod

pengwang
źródło
Dzięki za pomoc, ale to nie pomaga bezpośrednio w ustawianiu domyślnego tła listitem za pomocą selektora.
shilgapira
2

Nie jestem pewien, jak osiągnąć zamierzony efekt za pomocą samego selektora - w końcu z definicji jest jeden selektor dla całej listy.

Możesz jednak kontrolować zmiany zaznaczenia i rysować, co chcesz. W tym przykładowym projekcie ustawiam przezroczystość selektora i rysuję pasek na wybranym elemencie.

CommonsWare
źródło
Rzeczywiście, absolutnie sensowne jest, aby istniał tylko jeden selektor. Wydaje mi się, że jestem (lub byłem) trochę zdezorientowany istnieniem <item android:state_focused="false" android:drawable="@android:color/transparent" />klauzuli w selektorze domyślnym.
shilgapira
Mark, bawię się (mam ten sam problem co Gil). Wypróbowałem twoje rozwiązanie, jest to tylko częściowe tylko dla track ball, noneSeelected jest wywoływane, gdy dotykasz przedmiotów za pomocą dotyku, a onItemClicked jest uruchamiany, jeśli chcesz czegoś takiego, najprawdopodobniej musisz śledzić wybrane i wciśnięte pozycje wewnątrz ListView i narysować to, czego potrzebujesz w samym elemencie (uczyń go niestandardowym widokiem) oraz w dispatchDraw w ListView.
codeScriber
@codeScriber: "noneSeelected jest wywoływane, gdy dotykasz elementów za pomocą tylko dotyku, a onItemClicked jest uruchamiany" - jest to całkowicie normalne zachowanie, ponieważ dotyk nie ma nic wspólnego z zaznaczaniem.
CommonsWare
2

Zawsze używam tej samej metody i działa zawsze i wszędzie: po prostu używam takiego selektora

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

i ustaw w ListView (BARDZO WAŻNE, ABY TO DZIAŁAŁO) atrybut

android:choiceMode="singleChoice"

dla textColor po prostu umieść w folderze kolorów (WAŻNE, nie do rysowania!) selektor taki jak ten

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

to przykładowy szablon wiersza

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

wszystko pokazuje pracę bez żmudnych obejść. Mam nadzieję, że to pomoże

Apperside
źródło
-3

Zespół FrostWire tutaj.

Wszystkie badziewne api selektora nie działają zgodnie z oczekiwaniami. Po wypróbowaniu wszystkich rozwiązań przedstawionych w tym wątku do niczego, po prostu rozwiązaliśmy problem w momencie zawyżania elementu ListView.

  1. Upewnij się, że Twój przedmiot zachowuje swój stan, zrobiliśmy to jako zmienna składowa elementu MenuItem (wybrana wartość logiczna)

  2. Kiedy nadmuchujesz, zapytaj, czy element bazowy jest wybrany, jeśli tak, po prostu ustaw żądany zasób jako tło (czy to 9 łatka, czy cokolwiek). Upewnij się, że twój adapter jest tego świadomy i że wywołuje notifyDataChanged (), gdy coś zostało wybrane.

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

Byłoby naprawdę fajnie, gdyby selektory faktycznie działały, w teorii to ładne i eleganckie rozwiązanie, ale wygląda na to, że jest zepsute. POCAŁUNEK.

Gubatron
źródło
Chcę wypróbować to rozwiązanie, czy mógłbyś podać bardziej szczegółowe wyjaśnienie lub kompletne rozwiązanie? Dzięki
Andrew Lam