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.

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'
   
}

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>

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.

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


    }

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();
    }
}

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!

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

Por que não foi exibido nada? Não falamos para ele como queremos exibir os itens! Como assim?

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 :

<li> LinearLayoutManager - exibe itens em uma lista de rolagem vertical ou horizontal</li>

Screenshot_20160428-153107

<li> GridLayoutManager - exibe itens em uma grade.  </li>

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

    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!

    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.

    Próximo ArtigoQuando utilizar short circuit em Java