Fundo do item ListView via seletor personalizado


98

É possível aplicar um plano de fundo personalizado a cada item do Listview por meio do seletor de lista?

O seletor padrão especifica @android:color/transparentpara o state_focused="false"caso, mas alterar isso para algum drawable personalizado não afeta os itens que não são selecionados. Romain Guy parece sugerir nesta resposta que isso é possível.

No momento, estou conseguindo o mesmo efeito usando um plano de fundo personalizado em cada visualização e ocultando-o quando o item é selecionado / focado / seja o que for para que o seletor seja mostrado, mas seria mais elegante ter tudo isso definido em um só lugar.

Para referência, este é o seletor que estou usando para tentar fazer isso funcionar:

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

E é assim que estou configurando o seletor:

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

Agradecemos antecipadamente por qualquer ajuda!

Respostas:


128

Eu mesmo fiquei frustrado com isso e finalmente resolvi. Como Romain Guy sugeriu, há outro estado,, "android:state_selected"que você deve usar. Use um drawable de estado para o plano de fundo de seu item de lista e use um drawable de estado diferente para listSelectorsua lista:

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 que inclui o 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
Obrigado pela resposta detalhada. Como mencionei na minha pergunta, isso é exatamente o que estou fazendo no momento e funciona muito bem.
shilgapira

Infelizmente, ainda estou tendo problemas para personalizar minha visualização de lista. Publiquei meus problemas específicos aqui: stackoverflow.com/questions/5426385/… ; Eu apreciaria qualquer contribuição que você possa ter.
LostNomad311

Não funciona quando você usa drawables de 9 remendos com preenchimento como plano de fundo. Acho que encontrei a solução definitiva, veja minha resposta
Michał K

Isso funciona para pós-ICS, mas para Gingerbread toda a lista é colorida com drawable pressionado ou selecionado.
gunar

Existe alguma solução para o pré-ICS?
Lonli-Lokli

78

A solução dglmtn não funciona quando você tem um drawable de 9 remendos com preenchimento como plano de fundo. Coisas estranhas acontecem, eu não quero nem falar sobre isso, se você tem esse problema, você conhece.

Agora, se você quiser ter um listview com diferentes estados e drawables de 9 patch (ele funcionaria com qualquer drawables e cores, eu acho), você deve fazer 2 coisas:

  1. Defina o seletor para os itens da lista.
  2. Livre-se do seletor padrão da lista.

O que você deve fazer é primeiro definir o 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ão se esqueça do android:state_selected. Funciona como android:state_focusedpara a lista, mas é aplicado para o item da lista.

Agora aplique o seletor aos itens (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>

Faça um seletor transparente para a lista:

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

Isso deve fazer a coisa.


1
+1, não vi sua resposta antes, encontrei o mesmo resultado fazendo isso sozinho. Mas obrigado por mencionar os drawables de 9 patches, tenho certeza de que podem ser úteis em minhas implementações futuras. Essa deve ser a melhor resposta porque esse método é mais limpo e fácil de manter.
IsaacCisneros

28
Sinto muito, mas esse é o tipo de coisa que às vezes faz pensar por que essas malditas APIs de interface do usuário têm que ser tão ruins. O Android ainda tem muito que fazer. Essas coisas básicas não deveriam ser uma PITA.
Gubatron

2
+1 Obrigado, obrigado, obrigado. Saltar por todos esses obstáculos para alcançar algo tão básico. Todos esses estados são muito confusos.
Moritz

3
Em vez de android: listSelector = "@ drawable / list_selector", basta escrever android: listSelector = "@ android: color / transparent". Não há necessidade de list_selector.xml extra.
Sofi Software LLC

1
Correto, o seletor de lista não destrutivo pode ser apenas @android: color / transparent em vez de seu próprio seletor xml. Apenas tentei. O que não funciona é @ null, o que me surpreende porque deve indicar: "nenhum seletor de lista" e como o seletor de lista é desenhado atrás do item, nada deve ser desenhado. Mas isso não funciona, o seletor padrão está naquela época ...
Zordid

17

Nunca, jamais use uma "cor de fundo" para suas linhas de listview ...

isso irá bloquear todas as ações do seletor (foi o meu problema!)

boa sorte!


3
Isso não é verdade. Basta usar um seletor como plano de fundo, não uma cor estática ou desenho
Michał K

isso significa "cor de fundo". aceitar completamente suas palavras.
cV2

Eu cheguei aqui porque preciso de um seletor agora que usei setBackgroundColorcom uma "cor". Sigh ... Use setBackgroundou de setBackgroundDrawableacordo com sua versão API com um seletor.
EpicPandaForce

5

O artigo "Por que minha lista está preta? Uma otimização do Android" no Android Developers Blog tem uma explicação completa de por que o plano de fundo da lista fica preto ao rolar. Resposta simples: defina cacheColorHint em sua lista como transparente (# 00000000).


1
Uma simplicidade exemplar, mas funciona muito bem. Obrigado por Romain Guy ... <ListView ... android: cacheColorHint = "# 00000000"> </ListView> É tudo!
Eric JOYÉ

Não agradeça a Romain Guy. Ele é parte do problema. Não deveria haver tantos "truques ocultos" ou outros hacks em um sistema operacional como este. Embora eu ame o Android, ele é uma grande pilha de lixo quando comparado com outros SDKs (mesmo os mais novos / menos maduros!).
StackOverflowed em

5

É o suficiente, se você colocar 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
incrível ... exemplo mais simples
Shihab Uddin

1
E também tornar o seletor de
exibição de

4

ao invés de:

android:drawable="@color/transparent" 

escrever

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

4

Você pode escrever um 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 é seu seletor personalizado. Lamento, mas não sei escrever meu código


Obrigado pela ajuda, mas isso não ajuda diretamente na configuração do plano de fundo listitem padrão por meio do seletor.
shilgapira

2

Não tenho certeza de como alcançar o efeito desejado por meio do próprio seletor - afinal, por definição, há um seletor para toda a lista.

No entanto, você pode obter controle sobre as alterações de seleção e desenhar o que quiser. No presente projeto de exemplo , eu faço o seletor transparente e desenhar um bar no item selecionado.


Na verdade, faz todo o sentido haver apenas no máximo um seletor. Acho que estou (ou estava) um pouco confuso com a existência da <item android:state_focused="false" android:drawable="@android:color/transparent" />cláusula no seletor padrão.
shilgapira

Mark, brincando (tendo o mesmo problema que Gil) tentei sua solução, é apenas parcial para track ball apenas, nothingSeelected é chamado quando você toca em itens usando apenas toque e onItemClicked é disparado, se você quiser algo desse tipo, provavelmente você tem que manter o controle das posições selecionadas e pressionadas dentro do ListView e desenhar o que você precisa no próprio Item (torná-lo uma visualização personalizada) e no dispatchDraw do ListView.
codeScriber

@codeScriber: "nothingSeelected é chamado quando você toca em itens usando apenas toque e onItemClicked é disparado" - este é um comportamento perfeitamente normal, já que o toque não tem nada a ver com seleção.
CommonsWare

2

Eu sempre uso o mesmo método e funciona sempre, em qualquer lugar: eu simplesmente uso um seletor como este

<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 definir no ListView (ISTO É MUITO IMPORTANTE PARA FAZER FUNCIONAMENTO) o atributo

android:choiceMode="singleChoice"

para textColor basta colocar na pasta de cores (IMPORTANTE, não na pasta drawable!) um seletor como este

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

este é um modelo de linha de amostra

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

tudo mostrou funcionar sem soluções alternativas tediosas. espero que esta ajuda


-3

Equipe FrostWire aqui.

Todo o seletor crap api não funciona como esperado. Depois de tentar todas as soluções apresentadas neste tópico sem sucesso, apenas resolvemos o problema no momento de inflar o Item ListView.

  1. Certifique-se de que seu item mantenha seu estado, fizemos isso como uma variável membro do MenuItem (booleano selecionado)

  2. Ao inflar, pergunte se o item subjacente está selecionado, em caso afirmativo, apenas defina o recurso drawable que deseja como plano de fundo (seja um 9 patch ou qualquer outro). Certifique-se de que seu adaptador esteja ciente disso e que chame notificarDataChanged () quando algo for selecionado.

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

Seria muito bom se os seletores funcionassem, em teoria é uma solução bonita e elegante, mas parece que está quebrado. BEIJO.


Eu quero tentar essa solução, você poderia dar uma explicação mais detalhada ou uma solução completa? Obrigado
Andrew Lam
Ao utilizar nosso site, você reconhece que leu e compreendeu nossa Política de Cookies e nossa Política de Privacidade.
Licensed under cc by-sa 3.0 with attribution required.