Post atualizado em 06/09/2011.
O widget ListView é um componente poderoso e flexível do Android, normalmente utilizado para exibir uma simples lista de dados. Este post descreve como criar um ListAdapter para exibir várias informações e uma figura em cada linha do ListView.
Para demonstrar a criação do ListAdapter customizado foi criado um projeto para exibir em um ListView os estados do Brasil, juntamente com a abreviação, nome da capital, área em km2 e a imagem da bandeira do estado. O projeto rodando apresenta a seguinte tela:
O projeto possui somente uma Activity que carrega o ListView no método OnCreate() demonstrado no arquivo Main.java.
public class Main extends ListActivity {
/** Called when the activity is first created. */
@Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
List<State> stateList = new ArrayList<State>();
for (int i = 0; i < states.length; i++) {
State state = new State();
state.setState(states[i][0]);
state.setAbbreviation(states[i][1]);
state.setCapital(states[i][2]);
state.setArea(Float.parseFloat(states[i][3]));
state.setBanner(images[i]);
stateList.add(state);
}
setListAdapter(new StateAdapter(this, stateList));
}
private String[][] states = new String[][]{
{"Acre", "AC", "Rio Branco", "152581.4"},
{"Alagoas", "AL", "Maceió", "27767.7"},
{"Amapá", "AP", "Macapá", "142814.6"},
{"Amazonas", "AM", "Manaus", "1570745.7"},
{"Bahia", "BA", "Salvador", "564692.7"},
{"Ceará", "CE", "Fortaleza", "148825.6"},
{"Distrito Federal", "DF", "Brasília", "5822.1"},
{"Espírito Santo", "ES", "Vitória", "46077.5"},
{"Goiás", "GO", "Goiânia", "340086.7"},
{"Maranhão", "MA", "São Luís", "331983.3"},
{"Mato Grosso", "MT", "Cuiabá", "903357.9"},
{"Mato Grosso do Sul", "MS", "Campo Grande", "357125.0"},
{"Minas Gerais", "MG", "Belo Horizonte", "586528.3"},
{"Pará", "PA", "Belém", "1247689.5"},
{"Paraíba", "PB", "João Pessoa", "56439.8"},
{"Paraná", "PR", "Curitiba", "199314.9"},
{"Pernambuco", "PE", "Recife", "98311.6"},
{"Piauí", "PI", "Teresina", "251529.2"},
{"Rio de Janeiro", "RJ", "Rio de Janeiro", "43696.1"},
{"Rio Grande do Norte", "RN", "Natal", "52796.8"},
{"Rio Grande do Sul", "RS", "Porto Alegre", "281748.5"},
{"Rondônia", "RO", "Porto Velho", "237576.2"},
{"Roraima", "RR", "Boa Vista", "224299.0"},
{"Santa Catarina", "SC", "Florianópolis", "95346.2"},
{"São Paulo", "SP", "São Paulo", "248209.4"},
{"Sergipe", "SE", "Aracaju", "21910.3"},
{"Tocantins", "TO", "Palmas", "277620.9"}
};
private int[] images = new int[]{
R.drawable.acre,
R.drawable.alagoas,
R.drawable.amapa,
R.drawable.amazonas,
R.drawable.bahia,
R.drawable.ceara,
R.drawable.distritofederal,
R.drawable.espiritosanto,
R.drawable.goias,
R.drawable.maranhao,
R.drawable.matogrosso,
R.drawable.matogrossosul,
R.drawable.minasgerais,
R.drawable.para,
R.drawable.paraiba,
R.drawable.parana,
R.drawable.pernambuco,
R.drawable.piaui,
R.drawable.riojaneiro,
R.drawable.riograndenorte,
R.drawable.riograndesul,
R.drawable.rondonia,
R.drawable.roraima,
R.drawable.santacatarina,
R.drawable.saopaulo,
R.drawable.sergipe,
R.drawable.tocatins
};
}
No método OnCreate() foi criado um ArrayList com as informações dos estados. Normalmente este tipo de informação é carregada de uma base de dados ou de alguma outra fonte de informação, mas como o objetivo é demonstrar a utilização do ListView, foi criado um array com as informações dos estados (states) e outro array com as imagens das bandeiras (images). As imagens foram obtidas dos arquivos colocados na pasta de resource res/drawable-mdpi.
Para montar o ArrayList foi utilizada a classe State, descrita no arquivo State.java:
public class State {
private String state;
private String abbreviation;
private String capital;
private float area;
private int banner;
public String getState() {
return state;
}
public void setState(String state) {
this.state = state;
}
public String getAbbreviation() {
return abbreviation;
}
public void setAbbreviation(String shortening) {
this.abbreviation = shortening;
}
public String getCapital() {
return capital;
}
public void setCapital(String capital) {
this.capital = capital;
}
public float getArea() {
return area;
}
public void setArea(float area) {
this.area = area;
}
public int getBanner() {
return banner;
}
public void setBanner(int banner) {
this.banner = banner;
}
}
O cursor para as informações do ListView é montado utilizando o método setListAdapter(ListAdapter adapter) da classe ListActivity. No parâmetro adapter foi utilizada a classe StateAdapter que recebe como parâmetros o contexto da Activity e o ArrayList com as informações dos estados. O arquivo StateAdapter.java descreve os métodos que são necessários para montar o cursor do ListView.
/**
* Adapter utilizado para exibir as informações dos Estados
* no ListView.
* @author Administrador
*
*/
public class StateAdapter extends BaseAdapter {
private Context context;
private List<State> stateList;
public StateAdapter(Context context, List<State> statelist){
this.context = context;
this.stateList = statelist;
}
@Override
public int getCount() {
return stateList.size();
}
@Override
public Object getItem(int position) {
return stateList.get(position);
}
@Override
public long getItemId(int position) {
return position;
}
@Override
public View getView(int position, View convertView, ViewGroup parent) {
// Recupera o estado da posição atual
State state = stateList.get(position);
// Cria uma instância do layout XML para os objetos correspondentes
// na View
LayoutInflater inflater = (LayoutInflater)
context.getSystemService(Context.LAYOUT_INFLATER_SERVICE);
View view = inflater.inflate(R.layout.listview_states, null);
// Estado - Abreviação
TextView textState = (TextView)view.findViewById(R.id.textState);
textState.setText(state.getState() + " - " + state.getAbbreviation());
// Capital
TextView textCapital = (TextView)view.findViewById(R.id.textCapital);
textCapital.setText(state.getCapital());
// Área
TextView textArea = (TextView)view.findViewById(R.id.textArea);
textArea.setText(String.valueOf(state.getArea()));
// Bandeira
ImageView img = (ImageView)view.findViewById(R.id.imageState);
img.setImageResource(state.getBanner());
return view;
}
}
A classe StateAdapter extende a classe BaseAdapter que deve implementar os métodos:
- getCount(): retorna o número de itens.
- getItem(int position): retorna o item de uma posição específica.
- getItemId(int position): retorna o Id de um item de uma posição específica.
- getView(int position, View convertView, ViewGroup parent): retorna a View com as informações posicionadas de acordo com o layout montado no arquivo listview_states.xml
O arquivo listview_states.xml monta o layout que é utilizado em cada linha do ListView, para posicionar as informações em um formato customizado.
<?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:padding="5dp"
android:background="#cccccc"
>
<ImageView
android:id="@+id/imageState"
android:layout_width="60dp"
android:layout_height="42dp"
android:background="#ffffff"
android:scaleType="centerCrop"
/>
<TextView
android:id="@+id/textState"
android:layout_width="fill_parent"
android:layout_height="wrap_content"
android:layout_toRightOf="@+id/imageState"
android:layout_alignTop="@+id/imageState"
android:text="São Paulo - SP"
android:textSize="20sp"
android:textColor="#333333"
android:paddingLeft="5dp"
/>
<TextView
android:id="@+id/textCapital"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_below="@+id/textState"
android:layout_toRightOf="@+id/imageState"
android:text="São Paulo"
android:textColor="#333333"
android:paddingLeft="5dp"
/>
<TextView
android:id="@+id/textArea"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_below="@+id/textState"
android:layout_alignParentRight="true"
android:text="100000"
android:textColor="#333333"
/>
</RelativeLayout>
No método getView utilizando a classe LayoutInflater e a classe View podemos associar cada widget do layout à informação que deve ser exibida no ListView.
No exemplo deste post, quando um item do ListView recebe o foco a tradicional cor “Laranja” de fundo não aparece. Isto acontece porque o ListView possui uma cor de fundo opaca, neste caso “cinza”. Na verdade o foco “Laranja” é exibido, mas somente atrás do background do ListView.
Uma possível solução para exibir o fundo “Laranja” neste caso, é fazer uma pequena alteração da definição do layout da tela.
Primeiro precisamos criar um arquivo tipo StateList para definir as cores que serão apresentadas quando o item do ListView for pressionado, selecionado ou receber foco. Para isso foi criado o arquivo list_selector.xml, que deve ser colocado no diretório res/drawable
<?xml version="1.0" encoding="utf-8"?>
<selector
xmlns:android="http://schemas.android.com/apk/res/android">
<item
android:state_pressed="true"
android:drawable="@android:color/transparent" />
<item
android:state_selected="true"
android:drawable="@android:color/transparent" />
<item
android:state_focused="true"
android:drawable="@android:color/transparent" />
<item
android:drawable="@color/cinza" />
</selector>
Os itens relativos aos estados pressionado, selecionado ou com focus devem ser configurados com o atributo android:drawable com cor transparente, somente o último item que representa o item sem mudança de estado deve ser configurado com a cor cinza. A cor cinza foi declarada no arquivo color.xml que deve ser colocado no diretório res/values.
<?xml version="1.0" encoding="utf-8"?>
<resources>
<color name="cinza">#cccccc</color>
</resources>
Para que o fundo “laranja” funcione altere no arquivo listview_states.xml o atributo android:drawable da tag RelativeLayout de android:background="#cccccc" para android:background="@drawable/list_selector".
Agora quando um item for clicado teremos a seguinte tela:
Muito didático o exemplo, Rosana ! E pensar que procurei bastante em inglês para achar algo tão bem escrito em português ! Parabéns.
ResponderExcluirmuito bom o exemplo mesmo, só fiquei com uma dúvida, utilizei o exemplo acima para fazer uma lista parecida que eu precisava, sendo que minha lista não fica laranja quando clicamos no item, na verdade ela não fica de cor nenhuma, você pode me ajudar nessa informação?
ResponderExcluirFiz uma atualização do post apresentando uma possível solução para a exibição da cor laranja quando um item do ListView é clicado.
ResponderExcluirMuito legal esse post.
ResponderExcluirVocê acha dá para usar como menu principal da aplicação neste estilo ao invés de botões ?
Luis,
ResponderExcluirAcho que pode ser utilizado sim, sem problemas, inclusive você pode trabalhar o layout das linhas do listview. Dê uma olhada no post http://romarconsultoria.blogspot.com/2011/08/customizando-cor-de-fundo-de-um-widget.html, onde descrevo uma forma de alterar a cor de fundo quando um item é clicado. Para o ListView a utilização é similar a dos botões demonstrado no post.
Rosana,
ResponderExcluirFaço coro junto com o Marcelo. Procurei na internet e até achei exemplos em inglês mas não achei nada tão bem escrito e ilustrado.
Muito obrigado!
Muito bom o artigo. Porem, ficou uma dúvida. Como posso saber qual elemento da lista foi clicado?
ResponderExcluirRjun,
ResponderExcluirPara saber qual elemento da lista foi clicado, você deve chamar o método setOnItemClickListener do ListView e fazer o Override do método onItemClick, este método passa como parâmetros as informações de posição e do item clicado.
Você pode ver um exemplo da implementação destes métodos no post http://romarconsultoria.blogspot.com/2011/08/utilizando-o-listview-para-selecionar.html na classe Main.
Pergunta de iniciante: Onde o método getView é chamado?
ResponderExcluirA classe StateAdapter fornece acesso aos dados e também é responsável por fazer uma View para cada item do dataset, por isso o método getView é chamado em todos os momentos que os dados do ListAdapter forem utilizados, por exemplo no método onCreate o método getView é chamado para cada item da lista de estados.
ResponderExcluirOlá Rosana, parabenizo-lhe pelo Post.
ResponderExcluirTentei repetir este exemplo e na linha "View view = inflater.inflate(R.layout.listview_states, null);" da classe StateAdapater surge um erro, e as duas sugeridas para solucionar são: "Migrate Android Code e Rename.
Lembra-se de algo parecido em alguns dos seus códigos?
Atencipadamente agradeço pela atenção.
Abraço.
Ana Dolores
Ana,
ResponderExcluirNão vi este erro ainda. Você pode postar a mensagem de erro que você está recebendo?
Rosane, primeiramente parabéns pelo post, me ajudou muito..
ResponderExcluirMas uma dúvida ainda está me dando trabalho.
A parte de selecionar e ficar laranja simplesmente não deu certo.
Está faltando algo?