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

Fique por dentro

(Última atualização em: 15 de fevereiro de 2017)

  • 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.

  • Raphael Lourenco Braga

    Olá Matheus!
    Sou aluno da Alura e tinha implementado uma lista de Alunos conforme ensinado, através de uma ListView e usando um DAO que é alimentado por uma Activity Formulário.
    Estou seguindo suas orientações para usar o RcyclerView e não tive nenhum erro, porém quando subo a aplicação não está trazendo os livros cadastrados através do formulário. Pode me ajudar com o que posso ter feito de errado?

    Obrigado!

    https://uploads.disquscdn.com/images/0c03c8adc4baa4df1149e121b637f1a5f189a79c935e270d0c6b0c7c3eda16e1.png

    https://uploads.disquscdn.com/images/056d21c174c30bbbdcc09216404b2d6ce9ab871080c5874f8862529483779919.png

    • Matheus Brandino

      Fala aí Raphael, tudo bem ?

      Cara provavelmente você pode estar com algum problema na hora que defini o seu adapter na activity !

      • Raphael Lourenco Braga

        Falaí Matheus!
        Poxa, fiquei até surpreso com seu rápido retorno, queria até agradecer por compartilhar esse post que já está sendo de grande utilidade… 🙂
        Mas então, realmente imagino que seja exatamente isso que vc está dizendo, por isso mandei minhas telas, pq realmente não estou conseguindo enxergar aonde estou errando…
        Se puder dar uma olhada rápida no código q t mandei, serei muito grato!!!
        Um grande abraço.

  • Bárbara Maiza

    desculpa pela pergunta, mas onde é a lib pra eu colocar as dependencias??

    • Bárbara Maiza

      eu já achei, mas como adicionar isso na libs?

      • Logan

        No arquivo “build.gradle(Module:App)” – esse é o nome que aparece usando o Android Studio.

        Procure pela linha:
        dependencies {
        //… talvez já tenhas outras aqui …
        //Basta colar aqui
        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’

        }

        Lembrando que já há versões mais atuais.
        DICA: todas as dependências devem ter a mesma versão (23.3.0 ou seja ela qual for)

  • Hipnus

    Olá !

    Eu sou novato com Android e gostaria de saber aonde foi declarado os componentes que vão ficar no card layout.

    nome = (TextView) view.findViewById(R.id.item_livro_nome);
    preco = (TextView) view.findViewById(R.id.item_livro_preco);
    descricao = (TextView) view.findViewById(R.id.item_livro_descricao);
    autor = (TextView) view.findViewById(R.id.item_livro_autor);

    • David Coelho

      Também estou com essa dúvida. Não encontrei ainda o R.layout.item_livro.

      • Logan

        Basta criar um xml comum na pasta Layout contendo os TextView com ID item_livro_nome, item_livro_preco etc

    • Logan

      Esses TextViews estão no layout “item_livro.xml”. Ele não mostrou mas está escrito nessa parte do código:

      View view = LayoutInflater.from(context)
      .inflate(R.layout.item_livro, parent, false);

      Nessa parte do código você define qual é o layout dos itens da lista, lembrando que é muito importante se atentar em colocar os IDs corretos dos TextViews

  • Vinnycius Amâncio

    Olá, poderia disponibilizar o código?

  • Bruno Loducca

    Quando eu executei o meu app. Já tendo escrito o código, o meu recyclerView está ‘setado’ para popular os itens na posição horizontal. Portanto quando o faz isso ele deixa cada item muito distante um do outro, e não com a distância que havia determinado no layout. Alguém poderia me ajudar ?

  • Marina Alcântara

    Olá Matheus, sou aluna do Alura e suas explicações são ótimas. Será que poderia me ajudar?
    Fiz uma search view, que deve fazer uma busca em uma lista. Porém a lista só deve ser exibida após começar a digitar, pois antes será exibida uma outra mensagem. Eu fiz o xml da lista já, porém não sei como fazer a chamada da lista pelo search view apenas quando digitar algo. Por favor, poderia me ajudar?

  • Natanael Oliveira

    Como faz para exibir os dados que estão cadastrados do firebase?

Próximo ArtigoQuando utilizar short circuit em Java