sexta-feira, 4 de novembro de 2016

Criando um ListView customizado

Usa-se o ArrayAdapter para ListViews bem simples em que seja necessário exibir apenas TextViews que são preenchidos com o toString() do objeto. Enquanto que, para se criar ListViews complexas, é necessário desenvolver um adapter customizado. Mas, para isso, é importante conhecer como funciona a interação entre a ListView e o adapter.

A ListView pergunta ao adapter quantas linhas ele terá que exibir e solicita, para cada objeto da lista, sua representação visual, que retornará uma View preenchida com as informações do objeto.

Para criar um adapter customizado é preciso fazer uma classe que herde da classe BaseAdapter, que é a superclasse de todos os adapters do Android. Essa superclasse possui um construtor com dois atributos: um contexto e uma lista de objetos que se quer exibir. O contexto é usado para carregar os recursos da aplicação (layout, imagens...), além de guardar uma referência para a activity que é passada como parâmetro no construtor.

Ao criar um adapter, tem-se que implementar 4 métodos: getCount(), getItem(int), getItemId(int) e getView(int, View, ViewGroup).

getCount(): retorna a quantidade de linhas que a ListView exibirá (basta pegar o tamanho da lista de um determinado objeto);

Ex.
@Override
public int getCount() { return listaDeObjetos.size(); }


getItem(int): retorna um objeto, da lista de objetos, com base em sua posição;

Ex.
@Override
public Object getItem(int posicao) { return listaDeObjetos.get(posicao); }


getItemId(int): retorna um identificador único de um objeto da lista, como um id;

Ex.
@Override
public long getItemId(int posicao) { return posicao; }


getView(int, View, ViewGroup): método que gera a View que representará cada item da lista; esse processo é feito em 4 etapas:

1) Obter o objeto pela posição passada como parâmetro;
2) Carregar layout;
3) Preencher os componentes com os atributos do objeto;
4) Retornar layout com os componentes devidamente preenchidos com as informações do objeto.

Ex.
public View getView(int position, View convertView, ViewGroup parent) {
    // 1 passo
    MeuObjeto meuObjeto = listaObjetos.get(position);
    // 2 passo
    View linha = LayoutInflater.from(contexto).inflate(R.layout.item_lista, null);
    // 3 passo
    TextView nomeObjeto = (TextView) linha.findViewById(R.id.nome_objeto);
    nomeObjeto.setText(meuObjeto.getNome());
    // 4 passo
    return linha;
}


Para carregar o arquivo de layout, utiliza-se a classe LayoutInflater, que, ao chamar o método inflate(int, Viewgroup) retorna o elemento-raiz do mesmo. Com esse objeto, consegue-se obter a referência dos componentes que ele contém usando o findViewById(int). Uma vez tendo as instâncias dos componentes, basta preencher cada um com as informações do objeto.

O layout da linha pode ser feito da maneira que você quiser. No exemplo acima, o "R.layout.item_lista" só possuia um único TexView, que foi vinculado ao TextView nomeObjeto e depois setado pelo setText. Mas, poderia ter sido criado um layout com uma ImageView e mais dois TextViews, por exemplo. Então, no passo 3, a imagem e os TextViews deveriam ser setados, para que a view linha seja retornada completamente preenchida.

Como a listView só mostra os itens que cabem na tela, não é necessário instanciar todas as views existentes para cada item da lista, apenas as que estão sendo mostradas. Além disso, as views que foram mostradas, mas que, após os rolamento da lista, deixaram de ser mostradas, podem ser utilizadas novamente para apresentar novos itens, sendo necessário apenas uma atualização de seus dados. É para isso que existe o segundo parâmetro do getView: "View convertView". Esse parâmetro é passado pelo Android e faz referência a view que foi instanciada e visualizada na tela, mas que agora não está mais visível devido ao rolamento da listView. Segue um código de utilização desse parâmetro:

    @Override
    public View getView(int position, View convertView, ViewGroup parent) {
        View linha;
       
        if(convertView == null) {
            linha = activity.getLayoutInflater().inflate(R.layout.item_lista, parent, false);
        } else {
            linha = convertView;
        }


    TextView nomeObjeto = (TextView) linha.findViewById(R.id.nome_objeto); 
        MeuObjeto meuObjeto = (MeuObjeto) getItem(position);
        nomeObjeto.setText(meuObjeto.getNome());

        return linha;
    }


Feito o adapter customizado, tem-se que utilizá-lo como parâmetro de criação de uma listView. Para isso, basta colocar no onCreate da activity um código parecido com esse:

ListView listView = new ListView(this);
MeuAdapter adapter = new MeuAdapter(this, ListaObjetos);
listView.setAdapter(adapter);


Sempre que a lista de objetos do adapter for modificada, no caso de um acréscimo de um novo item, por exemplo, é necessário informar à listView que isso ocorreu. Para isso, usa-se o método notifyDataSetChanged(). Veja abaixo um exemplo de uso desse método:

    public void setItems(List<MeuObjeto> novaLista) {
        listaObjetos.clear();
        listaObjetos.addAll(novaLista);

        notifyDataSetChanged();
    }


Esse método setItems pode ser criado dentro do adapter. Assim, sempre que se queira modificar a lista de objetos do adapter, basta utilizá-lo, pois ele limpa uma lista de objeto e adiciona uma nova. Ou seja, se a lista foi modificada ao ser adicionado um novo item, a antiga lista será limpa e substituída pela novaLista. Ao final, informa-se à listView que os dados foram modificados pelo notifyDataSetChanged.

Sugestão de leitura: Utilizando o padrão ViewHolder.

Nenhum comentário:

Postar um comentário