Criando listas com RecyclerView
Quantas vezes caímos na situação de necessitarmos de uma lista em nosso aplicativo?
Vimos uma forma bem simples de implementar uma lista no Android com ListView
e Adapter
. Além disso, vimos como melhorá-la, ou seja, personalizá-la com um Adapter
personalizado e como reaproveitamos as View
s por meio do ViewHolder
.
Você pode conferir todos esses tópicos com mais detalhes nesse artigo!.
Mesmo sendo pontos importantes para a implementação de uma lista no Android, reparou o tanto de passos que realizamos para criar uma lista "ideal" para a nossa app? Será que não existe uma forma melhor?
Para resolver esse problema de não sermos obrigados a manter a boa prática sem a preocupação com alguns detalhes (que veremos no decorrer do post) foi criado uma nova View
para listas.
Para começarmos a utilizar, temos que trazer a lib como dependência do projeto:
dependencies {
compile 'com.android.support:appcompat-v7:23.3.0' compile 'com.android.support:design:23.3.0' compile 'com.android.support:recyclerview-v7:23.3.0' }
Inserindo o RecyclerView no layout
Já podemos começar a usar todos os recursos dessa biblioteca! Vamos iniciar colocando ela num layout:
<android.support.design.widget.CoordinatorLayout xmlns:android="http://schemas.android.com/apk/res/android" xmlns:app="http://schemas.android.com/apk/res-auto" android:layout_width="match_parent" android:layout_height="match_parent">
<android.support.v7.widget.RecyclerView android:id="@+id/recycler" android:layout_width="match_parent" android:layout_height="match_parent" />
</android.support.design.widget.CoordinatorLayout>
Pegando a referência do RecyclerView
Por enquanto nada muito diferente do ListView
: declaramos da mesma forma. Para usá-la precisaremos de uma referência para ela:
public class MainActivity extends AppCompatActivity {
@Override protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
RecyclerView recyclerView = (RecyclerView) findViewById(R.id.recycler); }
}
Tudo está bem tranquilo até o momento, mas ainda falta fazermos bastante coisa.
Criando uma entidade e lista de exemplo
Como estamos tratando de uma view que mostrará uma lista, em algum momento teremos que fazer essa lista surgir magicamente em nosso código. Vamos criar uma lista de livros, para isso criaremos uma classe Livro
:
public class Livro {
private final String nomeLivro;
private final String nomeAutor;
private final String descricao;
private final Double preco;
public Livro(String nomeLivro, String nomeAutor, String descricao, Double preco) {
this.nomeLivro = nomeLivro;
this.nomeAutor = nomeAutor;
this.descricao = descricao;
this.preco = preco; }
public String getNomeLivro() { return nomeLivro; }
public String getNomeAutor() { return nomeAutor; }
public String getDescricao() { return descricao; }
public Double getPreco() { return preco; } }
E agora vamos utilizar nossa lista de livros na nossa Activity
:
@Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.activity_main);
RecyclerView recyclerView = (RecyclerView) findViewById(R.id.recycler);
List<Livro> livros = // recupera do banco de dados ou webservice }
Implementando o adapter do RecyclerView
Está faltando pegarmos essa lista e a colocarmos para ser exibida, da mesma forma que ListView, mas como transformávamos mesmo nossos objetos em Views? Precisávamos criar um Adapter:
@Override protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
RecyclerView recyclerView = (RecyclerView) findViewById(R.id.recycler);
List<Livro> livros = // recupera do banco de dados ou webservice
recyclerView.setAdapter(new NossoAdapter(livros));
}
Para nosso código compilar, o objeto que precisamos passar deve ser um Adapter. Então, nossa classe precisará ser um também :
public class NossoAdapter extends RecyclerView.Adapter {
public NossoAdapter(List<Livro> livros) {
} }
Para nossa classe ser um Adapter
, temos que sobreescrever alguns métodos:
public class NossoAdapter extends RecyclerView.Adapter {
public NossoAdapter(List<Livro> livros) {
}
@Override public RecyclerView.ViewHolder onCreateViewHolder(ViewGroup parent, int viewType) { return null; }
@Override public void onBindViewHolder(RecyclerView.ViewHolder viewHolder, int position) {
}
@Override public int getItemCount() { return 0; } }
Repare que nos deparamos com três métodos novos: onCreateViewHolder, onBindViewHolder, getItemCount, vamos entender um pouco mais sobre eles.
O mais fácil de matarmos logo de cara é o getItemCount, que é a quantidade de itens que teremos, então vamos deixar nosso código certo:
public class NossoAdapter extends RecyclerView.Adapter {
private List<Livro> livros;
public NossoAdapter(List<Livro> livros) { this.livros = livros; }
@Override public RecyclerView.ViewHolder onCreateViewHolder(ViewGroup parent, int viewType) { return null; }
@Override public void onBindViewHolder(RecyclerView.ViewHolder viewHolder, int position) {
}
@Override public int getItemCount() { return livros.size(); } }
Implementando o ViewHolder do RecyclerView
Vamos partir agora para o onCreateViewHolder, que é o método que criamos o ViewHolder
, que fazíamos por boa prática no ListView
. Além disso, é neste método que devemos inflar a view para vincular ao ViewHolder
.
Como esse ViewHolder
saberá fazer os findViewById que precisaremos? Ele é muito genérico, não conseguirá resolver este problema!
Mas nós, como ninjas da programação orientada a objetos, sabemos como resolver: iremos criar uma classe que herde desse tipo mais genérico e que faça o que nós precisamos! Veja:
public class NossoViewHolder extends RecyclerView.ViewHolder {
}
O compilador está reclamando que a classe pai tem um construtor que precisa de uma View
. Vamos solucionar isso:
public class NossoViewHolder extends RecyclerView.ViewHolder {
final TextView nome;
final TextView descricao;
final TextView preco;
final TextView autor;
public NossoViewHolder(View view) {
super(view);
nome = (TextView)
view.findViewById(R.id.item_livro_nome); // restante das buscas }
}
Retornando ao problema inicial, agora podemos solucionar instanciando nosso ViewHolder
:
@Override public RecyclerView.ViewHolder onCreateViewHolder(ViewGroup parent, int viewType) {
NossoViewHolder holder = new NossoViewHolder();
return holder; }
Precisamos passar uma View
para nosso código compilar:
@Override public RecyclerView.ViewHolder onCreateViewHolder(ViewGroup parent, int viewType) {
View view = LayoutInflater.from(context).inflate(R.layout.item_livro, parent, false);
NossoViewHolder holder = new NossoViewHolder(view);
return holder; }
Perceba que precisamos providenciar um Context
para que nosso LayoutInflater
possa funcionar direitinho :
public class NossoAdapter extends RecyclerView.Adapter {
private List<Livro> livros; private Context context
public NossoAdapter(List<Livro> livros, Context context) {
this.livros = livros;
this.context = context;
}
// restante do código
}
E na nossa Activity
:
@Override protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
RecyclerView recyclerView = (RecyclerView) findViewById(R.id.recycler);
List<Livro> livros = // recupera do banco de dados ou webservice
recyclerView.setAdapter(new NossoAdapter(livros, this)); }
Bacana, tudo certinho, agora basta vermos o que o último método faz e tudo certo!
Populando cada item do RecyclerView
E agora o que ficou faltando fazermos? Apenas popularmos cada item, com as informações do livro.
É exatamente o que faremos no método onBindViewHolder
, por isso recebemos um ViewHolder
, aquele que foi criado no método onCreateViewHolder
, com isso conseguimos popular de maneira que desejamos.
@Override public void onBindViewHolder(RecyclerView.ViewHolder viewHolder, int position) {
NossoViewHolder holder = (NossoViewHolder) viewHolder;
Livro livro = livros.get(position) ;
holder.nome.setText(livro.getNome()); //demais campos
}
Terminamos. Vamos rodar, tirando do banco de dados com registros ou do webservice e temos esse resultado:
Exibindo o itens do RecyclerView
Por que não foi exibido nada? Não falamos para ele como queremos exibir os itens! Como assim?
Definindo o layout dos itens
O RecyclerView tem uma característica muito bacana: ele tem vários layouts para apresentação dos itens e precisamos informar para ele qual é a estratégia que queremos usar!
Bom, temos as seguintes opções :
LinearLayoutManager
Exibe itens em uma lista de rolagem vertical ou horizontal
GridLayoutManager
Exibe itens em uma grade.
StaggeredGridLayoutManager
Exibe itens em uma grade escalonada.
Agora, basta escolher qual queremos utilizar e avisar para o RecyclerView
:
@Override protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
RecyclerView recyclerView = (RecyclerView) findViewById(R.id.recycler);
List<Livro> livros = // recupera do banco de dados ou webservice
recyclerView.setAdapter(new NossoAdapter(livros, this));
LayoutManager layout = new LinearLayoutManager(this, LinearLayoutManager.VERTICAL, false));
recyclerView.setLayoutManager(layout);
}
Implementando essas duas linhas e rodando nosso código a lista é exibida! :)
Ampliando mais o conhecimento
Finalizamos a nossa implementação de lista com o RecyclerView
, porém, além da implementação com o Java, é muito comum encontrarmos projetos desenvolvidos em Kotlin que fazem uso do RecyclerView
também.
Em outras palavras, vou aproveitar e compartilhar com vocês o artigo do Alex que explica sobre a implementação do RecyclerView
no Kotlin considerando as peculiaridades dessa nova linguagem.
Resumo
E aí pessoal, o que acharam da dica? Quando forem fazer seu aplicativo e ele precisar de uma lista, vão correr para onde?
Para o ListView
, que temos apenas que criar um Adapter
e temos que nos lembrar de fazer um ViewHolder
e ainda reaproveitar o convertView
?
Ou para o RecyclerView
, onde já há o encapsulamento do convertView
e temos que nos preocupar com a implementação do ViewHolder
, além de termos uma caracteristica bem interessante, que é a possibilidade de trocar a forma de exibição de maneira dinâmica, parecido com o Keep da Google (que troca a forma que os cards são exibidos), alterando apenas o tipo do LayoutManager
?
Quer saber mais de Android? Aqui na Alura temos uma formação android completa !