quinta-feira, outubro 18, 2012

Desenvolvendo para Android: O ListView

O ListView é um widget dos mais úteis, permitindo apresentar uma lista "rolável" de itens, onde cada item pode ser composto de vários widgets. É relativamente simples preencher um ListView com dados provenientes de uma matriz ou base de dados, através de um Adapter. Embora seja largamente utilizado, o ListView tem algumas características, digamos, irritantes.

A Coisa Fica Preta

Embora isto seja documentado (e portanto é uma feature e não um bug), é bem provável que na sua primeira tentativa de usar um ListView você observe um efeito curioso: ao rolar a lista parte dela fica preta. Um efeito curioso, mas que inutiliza a sua lista (principalmente se o seu texto estiver em preto).

A explicação é que, por default, a ListView está otimizada para fundo preto. "Hello, Mr Google Engineer!": boas otimizações não impedem o funcionamento dos casos não otimizados!

A solução é simples (depois que você sabe): acrescente o parâmetro cacheColorHint na definição da ListView:

android:cacheColorHint="#00000000"

Usando Botões em Uma ListView

Uma vantagem de uma ListView é que você tem controle sobre os widgets usados para cada item. Você pode criar um layout para isto, de forma semelhante ao que faz para um intent. A coisa complica quando você resolve colocar no layout algum widget que gere um evento, como um botão.

O problema é que o listener do evento não tem, a princípio, como saber em qual item o evento foi disparado. Os widgets dos diversos itens são criados com o mesmo ID.

Eu achei uma solução complicada, que inclui até uma rotina recursiva. A ideia básica é colocar a identificação da linha no campo tag das views que a compõem. Fiz uma tentativa de mudar o ID dinamicamente, mas não funcionou. Eu acabei mantendo a rotina recursiva, mas aproveitei para associar também listeners ao botões:
public final class ClickableButtonListAdapter extends SimpleAdapter {

  public static final String HASHMAP_ID = "_id";
  private OnClickListener[] btnListener;
  private int btnId[];

  public ClickableButtonListAdapter(Context context,
           List<? extends Map<String, ?>> data, 
           int resource, String[] from, int[] to) {
    super(context, data, resource, from, to);
    btnId = null;
  }

  // construtor com listas de ids dos botões e listeners associados
  public ClickableButtonListAdapter(Context context,
           List<? extends Map<String, ?>> data, 
           int resource, String[] from,
           int[] to, int[] btn, OnClickListener[] listener) {
    super(context, data, resource, from, to);
    btnListener = listener;
    btnId = btn;
  }

  @SuppressWarnings("unchecked")
  @Override
  public View getView(int position, View convertView, ViewGroup parent) {
    // Identifica os botões
    View view = super.getView(position, convertView, parent);
    String tag = ((HashMap<String, String>)getItem(position)).get(HASHMAP_ID);
    setViewTag(view, tag);
    // Associa os listeners
    if (btnId != null) {
      for (int i = 0; i < btnId.length; i++) {
        Button btn = (Button) view.findViewById(btnId[i]);
        if (btn != null)
          btn.setOnClickListener(btnListener[i]);
      }
    }
  }

  // Rotina auxiliar para identificar os elementos da lista
  private void setViewTag(View view, Object tag) {
    view.setTag(tag);
    if (view instanceof ViewGroup) {
      for (int i = 0; i < ((ViewGroup) view).getChildCount(); i++) {
        setViewTag(((ViewGroup) view).getChildAt(i), tag);
      }
    }
  }
}

Bonus: Como Limpar o Estado de um RadioButton

No meu caso, os botões nos itens do ListView eram radio buttons, isto é, um conjunto de botões onde, quando você marca um, os outros (do mesmo item) são automaticamente desmarcados. Existe, porém uma pequena limitação nos radio buttons do Android: não existe função para desmarcá-lo. Ou seja, uma vez que o operador fez uma seleção não teria como voltar a trás.

As solução é simples: acrescente mais um radio button ao grupo, mas deixe-o invisível. Para limpar os radio buttons do grupo, marque o invisível (isto você consegue fazer por programa).


Se eu tiver paciência coloco um exemplo de tudo isto no próximo post.

Nenhum comentário: