Criando listas com RecyclerView

(Última atualização em: 15 de fevereiro 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 RecyclerView.

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

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!

  • Ailton

    Excelente!!!
    Muito legal e intuitivo, parabéns!!

    Poderia fazer um post sobre o context menu em recycler view?
    Valeu!!

    • Matheus Brandino

      Olá Ailton,

      Obrigado !!!!

      Está na lista já de posts a serem escritos !

      Fica atento que logo logo deve sair !

      Abraços

  • Muito bom Matheus! Parabéns 😀

    • Matheus Brandino

      Obrigado Alex !

      😀 😀 😀 😀

  • Apolinário de Jesus Leal

    Olá Mateus, Ótimo tutorial, estou usando web service o que passo nesta linha
    List livros = // recupera do banco de dados ou webservice

    • Matheus Brandino

      Seu webservice deve retornar a listagem, imagino. Você vai precisar de uma lista para conseguir manipular!

      Espero ter ajudado.

      • Leal

        Olá Matheus obrigado meu caro pelo rápido retorno fiz conforme o tutorial (acho) mais não seu onde estou errando veja minha MainActivity por gentileza
        https://github.com/apolinarioleal/webapi/blob/master/MainActivity.java

        • Leal

          Tá mostrando a lista vasia

        • Matheus Brandino

          Cara, você tem vincular a lista que você busca no server, no seu adapter e vincular o adapter ao recyclerview..

          Dá uma olhadinha que estamos fazendo isso aqui no post, que dá para seguir…

          Abraços 😀

  • Carlos Alexandre

    Show!!! Gostaria de saber se é possível colocar o set onitemclickListener no adapter e dele chamar um fragment ou uma activity?

  • naraujo

    Ótima explicação, simples, clara e direta!

    • Matheus Brandino

      Muito obrigado !

      Espero ter ajudado bastante !

      Abraços

  • Jasian Pereira Cardoso

    boa tarde gostei muito da explicação, mas tenho uma duvida como faço para centralizar os itens usando o gridlayoutmanager https://uploads.disquscdn.com/images/f17f737259f362c021a3d7552c45cc4a7921f96ea5b4a33f3bd1278541dd4ab2.png ?

    quando tenho uma quantidade de objetos que não e multiplo de três não completa as três colunas como podemos ver na imagem acima.

    preciso que fique igual a imagem abaixo sempre que tenha dois ou um unico objeto naquela linha.
    https://uploads.disquscdn.com/images/9a9d8cfa5d569081a4605839bd2766822134406b418c9a28fa92a2c7cee7f5d7.png

    https://uploads.disquscdn.com/images/4f3e8ab3b7835bff93b465cb7491be7f7b02bd3d007906b198349a2e7a638239.png

    independente da quantidade de objetos que eu tenha na lista a ultima linha que não completa tem que ficar centralizado.

  • Pingback: Utilizando o padrão ViewHolder - Blog da Alura: desenvolvimento, design e muita tecnologia()

  • Felipe Thomas

    Olá Matheus. Muito bom o seu artigo.

    Gostaria de dar uma dica, no método onBindViewHolder você recebe o ViewHolder como parâmetro e logo após instancia o holder novamente, passando o ViewHolder que veio como parâmetro. Não é necessário instanciá-lo dentro do método, basta usar ele diretamente para setar os textViews e outros elementos.

    @Override
    public void onBindViewHolder(RecyclerView.ViewHolder viewHolder,
    int position) {

    Livro livro = livros.get(position) ;
    //aqui
    viewHolder.nome.setText(livro.getNome());

    //demais campos

    }

    • Matheus Brandinp

      Oi Felipe, cara obrigado pelo feedback !!

      Cara então, eu não estou instanciando o objeto, na verdade estou apenas convertendo o que recebemos para o que eu quero. Vou te explicar o real motivo. Por padrão é devolvido um objeto do tipo RecyclerView.ViewHolder, correto ? Essa classe não possui os meus atributos, logo quando fossemos tentar acessa-los teríamos problemas falando que a classe não tinha, por isso eu afirmo para o compilador que a classe que estou recebendo no parâmetro é do tipo NossoViewHolder, que por sua vez herda de RecyclerView.ViewHolder.

      • Felipe Thomas

        Perfeito. Compreendido. Vlwww.
        Mais uma vez, parabéns pelo artigo.

Próximo ArtigoQuando utilizar short circuit em Java