quarta-feira, 5 de outubro de 2011

Utilizando duas ListViews em uma mesma tela

Na grande maioria das vezes você vai precisar de apenas uma lista na tela, por isso os caras do Android fizeram o ListActivity. No entando, se você colocar mais de uma lista na tela, a sua app pode perder usabilidade, ou seja, seu usuário pode ficar confuso com tantas listas para dar scroll, além do que, em celulares com telas pequenas isso pode ser um problema.

Porém, sempre existe aquele caso isolado que você vai precisar utilizar duas ListViews na mesma tela. Foi pensando nisso que fiz esse post.

Neste projeto vamos criar 3 Classes:
  1. ListObjectViewActivity: Esta classe será a tela principal da nossa aplicação.
  2. Contatos: Esta classe é um POJO (Plain Old Java Object) que é uma classe que tem apenas atribudos privados com setter e getters. Cada contato terá um objeto Contatos que define o nome, o id e o telefone do respectivo contato.
  3. MeuAdapter: Esta será a classe adaptador, que vai preencher a ListView com meus objetos Contatos.
E dois arquivos de Layout:
  1. main.xml : Layout principal que conterá 2 TextViews e 2 ListViews
  2. linha.xml: Layout que será utilizado pelo adaptador para preencher as linhas dos ListViews

Vamos começar pelo mais simples que é a classe Contatos.

Contatos.java
public class Contatos {
 
 private int id;
 private String nome;
 private String telefone;
 
 public Contatos(int id, String nome, String telefone) {
  super();
  this.id = id;
  this.nome = nome;
  this.telefone = telefone;
 }

 public int getId() {
  return id;
 }

 public String getNome() {
  return nome;
 }

 public String getTelefone() {
  return telefone;
 }

}

Esta classe pode ter o que você quiser, endereço, email, qualquer informação que você quiser que apareça na lista.

Agora que criamos nossa classe Contatos, vamos criar um adaptador para esta classe. Um adaptador utiliza o Padrão de Projeto ADAPTER que é muito útil para transformar um objeto em outro. Ou melhor adaptar um objeto para uma determinada situação.

Para criar um adaptador você pode herdar da classe BaseAdapter. Depois disso você vai precisar implementar alguns métodos, são estes:


  1. int getCount(): Retorna o tamanho da lista que você vai adaptar 
  2. Object getItem(int position): Retorna um item da lista na posição "position".
  3. long getItemId: Retorna o id do item, não vamos utilizar este, pode retornar zero;
  4. View getView: É o método mais importante, é aqui que a ListView vai ser preenchida com objetos Contatos.

Veja como fica nosso adaptador.
MeuAdapter.java
public class MeuAdapter extends BaseAdapter {

 Context contexto;
 List lista;

 public MeuAdapter(Context contexto,List lista) {
  super();
  this.contexto = contexto;
  this.lista = lista;
 }

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

 @Override
 public Object getItem(int position) {
  return lista.get(position);
 }

 @Override
 public long getItemId(int arg0) {
  return 0;
 }

 @Override
 public View getView(int position, View convertView, ViewGroup parent) {
  
  Contatos contato = (Contatos)getItem(position);
  /**Utiliza o conceito de Adaptador Eficiente.
   * usando o convertView da forma que é feita é mais eficiente do que
   * usar View v = LayoutInflater.from ... porque dessa forma toda fez que
   * o usuário der scroll novas linhas vão ser criadas. E com o convertView
   * isso não acontece.
  **/
  if(convertView == null){
   
   convertView = LayoutInflater.from(contexto).inflate(R.layout.linha, null);
  }
  
  TextView tvNome = (TextView) convertView.findViewById(R.id.tvNome);
  TextView tvTelefone = (TextView) convertView.findViewById(R.id.tvTelefone);
  
  tvNome.setText(contato.getNome());
  tvTelefone.setText("Tel: "+contato.getTelefone());

  return convertView;
 }

}

A maioria dos adaptadores personalizados tem esta cara ai. o que vai mudar é o método getView. Nele cada linha é preenchida do jeito que o programador quiser. O importante detalhe é que o adaptador precisa de Layout que representada uma linha.

No nosso caso o layout tem apenas dois TextViews, um que irá preencher o nome e o outro o telefone. Mas poderia ter um icone, ter 3 textviews, só depende da necessidade do programador.
O Layout utilizado pelo nosso "Adapter" é:

linha.xml

 
 



Veja que em linha.xml tem apenas dois TextViews, caso você queira colocar uma imagem em cada linha da ListView basta colocar um ImageView no layout, e no método getView do adapter setar a imagem desejada para aquele objeto.

Agora vamos para nossa classe principal.
Primeiramente o main.xml para entendermos como ficará nossa tela:

main.xml

 
 
 
 


O código XML não fica tão legível devido a formatação. Cole esse código no layout/main.xml do seu projeto e dê um ctrl+shift+f para melhorar.

Bom este layout, como falei anteriormente, tem apenas 2 textViews e 2 ListViews.

Agora vamos ver a classe principal.

ListObjectViewActivity.java
public class ListObjectViewActivity extends Activity implements
  OnItemClickListener {

 @Override
 public void onCreate(Bundle savedInstanceState) {
  super.onCreate(savedInstanceState);
  setContentView(R.layout.main);

  // Criando lista de objetos para preenhcer ListView 1
  List contatos1 = new ArrayList();
  contatos1.add(new Contatos(1, "Androiano", "123456"));
  contatos1.add(new Contatos(2, "Renata", "12456"));
  contatos1.add(new Contatos(3, "Rodrigo", "1277856"));
  contatos1.add(new Contatos(4, "Robson", "152876"));
  contatos1.add(new Contatos(5, "Portal Android", "12345"));

  // Criando e setando o adaptador personalizado para Lista 1
  MeuAdapter adapter1 = new MeuAdapter(this, contatos1);
  ListView lista1 = (ListView) findViewById(R.id.listView1);
  lista1.setAdapter(adapter1);

  // Criando lista de objetos para preenhcer ListView 2
  List contatos2 = new ArrayList();
  contatos2.add(new Contatos(1, "Alex", "456775"));
  contatos2.add(new Contatos(2, "Fulano", "765656"));
  contatos2.add(new Contatos(3, "Pelé", "65556"));
  contatos2.add(new Contatos(4, "Android", "456876"));
  contatos2.add(new Contatos(5, "Linux", "3446"));

  // Criando e setando o adaptador personalizado para Lista 2
  MeuAdapter adapter2 = new MeuAdapter(this, contatos2);
  ListView lista2 = (ListView) findViewById(R.id.listView2);
  lista2.setAdapter(adapter2);

  // Adicionando Listeners aos ListViews
  lista1.setOnItemClickListener(this);
  lista2.setOnItemClickListener(this);

 }

 @Override
 public void onItemClick(AdapterView parent, View v, int position, long id) {
  Contatos contato = (Contatos) parent.getAdapter().getItem(position);

  Toast.makeText(this,
    "Nome: " + contato.getNome() + "\nTel:" + contato.getTelefone(),
    Toast.LENGTH_SHORT).show();

 }
}

Vamos analisar um pouco esse código.
O trecho abaixo é o que faz a mágica. A ListView precisa ser preenchida, mas se você passar um objeto Contatos ela não saberá como preencher as linhas com um objeto que ela desconhece. Ai entre o papel do adapter. O ListView não conhece Contatos, mas ele conhece BaseAdapter. Então basta você "transformar" seu objeto Contatos para BaseAdapter.


MeuAdapter adapter1 = new MeuAdapter(this, contatos1);
  ListView lista1 = (ListView) findViewById(R.id.listView1);
  lista1.setAdapter(adapter1);

É desta mesma forma que trabalho o ArrayAdapter do Android, que faz a adaptação de Strings para a ListView.

Para capturar eventos de click das listas eu implementei a interface OnItemClickListener que possui o método onItemClick. Neste método você implementa o comportamento do evento, no meu caso é pegar o objeto (Contatos) referente ao click e mostrar na tela o nome e o telefone do contato clicado.

Vamos ver o resultado final do projeto.


[ ]'s Androiano

2 comentários:

robsonfagundes disse...
Este comentário foi removido pelo autor.
robsonfagundes disse...

Parabéns Adriano {Androiano}
Vlw pelo post, Muito Esclarecedor...