Criando listas com RecyclerView

(Last Updated On: 7 de outubro de 2017)

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 Views por meio do ViewHolder.

Você pode conferir todos esses tópicos com mais detalhes nesse post!.

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, a <a href="http://developer.android.com/intl/pt-br/reference/android/support/v7/widget/RecyclerView.html" target="_blank">RecyclerView</a>.

Adicionando o RecyclerView no projeto

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:

Screenshot_20160428-155301

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

Screenshot_20160428-153107

GridLayoutManager

Exibe itens em uma grade.

Screenshot_20160428-153134

StaggeredGridLayoutManager

Exibe itens em uma grade escalonada.

Screenshot_20160428-155743

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! 🙂

Screenshot_20160428-160530

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 vai correr 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?

Aproveite e deixe o seu comentário sobre o que achou do RecyclerView e como foi sua experiência ao implementá-lo! 😀

Quer saber mais de Android? Dá uma olhadinha nos cursos que temos aqui no Alura e da Caelum!

Próximo ArtigoQuando utilizar short circuit em Java