Como criar uma lista com o RecyclerView

Como criar uma lista com o RecyclerView Parte do Android Jetpack.

Se o app precisa exibir uma lista de rolagem de elementos com base em grandes conjuntos de dados (ou dados que mudam com frequência), use RecyclerView conforme descrito nesta página.

Dica: comece com um código de modelo no Android Studio, clicando em File > New > Fragment > Fragment (List). Em seguida, basta adicionar o fragmento ao layout da atividade.

Figura 1. Uma lista usando RecyclerView.

Figura 2. Uma lista que também usa CardView.

Se você quiser criar uma lista com cards, conforme mostrado na figura 2, use também o widget CardView, como descrito em Criar um layout baseado em cards.

Se você quiser ver um exemplo de código para RecyclerView, confira o app RecyclerView de exemplo Java | Kotlin (links em inglês).

Visão geral do RecyclerView

O widget RecyclerView é uma versão mais avançada e flexível do ListView.

No modelo RecyclerView, diferentes componentes trabalham juntos para exibir seus dados. O contêiner geral da interface do usuário é um objeto RecyclerView que você adiciona ao layout. O RecyclerView é preenchido com visualizações fornecidas por um gerenciador de layout que você fornece. Você pode usar um dos nossos gerenciadores de layout padrão (por exemplo, LinearLayoutManager ou GridLayoutManager) ou implementar um gerenciador de layout próprio.

As visualizações na lista são representadas por objetos fixadores de visualização. Esses objetos são instâncias de uma classe que você define estendendo RecyclerView.ViewHolder. Cada fixador de visualização é responsável por exibir um único item com uma visualização. Por exemplo, se sua lista mostra uma coletânea de músicas, cada fixador de visualização pode representar um único álbum. O RecyclerView cria apenas quantos fixadores de visualização forem necessários para exibir a parte da tela do conteúdo dinâmico, além de alguns extras. À medida que o usuário rola a lista, o RecyclerView retira as visualizações da tela e as vincula novamente aos dados que estão rolando na tela.

Os objetos do fixador de visualização são gerenciados por um adaptador, que você cria estendendo RecyclerView.Adapter. O adaptador cria os fixadores de visualização conforme necessário. O adaptador também vincula os fixadores de visualização aos respectivos dados. Isso é feito atribuindo o fixador de visualização a uma posição e chamando o método onBindViewHolder() do adaptador. Esse método usa a posição do fixador de visualização para determinar qual precisa ser o conteúdo, com base na posição da lista.

Esse modelo RecyclerView realiza diversas otimizações para que você não precise fazer isso:

  • Quando a lista é preenchida pela primeira vez, ela cria e vincula alguns fixadores de visualização em ambos os lados da lista. Por exemplo, se a visualização estiver exibindo as posições de lista de 0 a 9, o RecyclerView criará e vinculará esses fixadores de visualização e também poderá criar e vincular o fixador de visualização para a posição 10. Dessa forma, se o usuário rolar a lista, o próximo elemento estará pronto para ser exibido.
  • À medida que o usuário rola a lista, o RecyclerView cria novos fixadores de visualização conforme necessário. Ele também salva os fixadores de visualização que rolaram para fora da tela para que possam ser reutilizados. Se o usuário mudar a direção que estava rolando, os fixadores de visualização que foram rolados para fora da tela poderão ser trazidos de volta. Por outro lado, se o usuário continuar rolando na mesma direção, os fixadores de visualização que estiverem fora da tela por mais tempo poderão ser redefinidos para novos dados. O fixador de visualização não precisa ser criado ou ter sua visualização ampliada; em vez disso, o app apenas atualiza o conteúdo da visualização para corresponder ao novo item ao qual estava vinculada.
  • Quando os itens exibidos mudarem, você poderá notificar o adaptador chamando um método RecyclerView.Adapter.notify…() apropriado. O código integrado do adaptador vincula novamente apenas os itens afetados.

Adicionar a Biblioteca de Suporte

Para acessar o widget RecyclerView, você precisa adicionar as Bibliotecas de Suporte v7 ao seu projeto da seguinte forma:

  1. Abra o arquivo build.gradle relativo ao módulo do seu app.
  2. Adicione a Biblioteca de Suporte à seção dependencies.
        dependencies {
            implementation 'com.android.support:recyclerview-v7:28.0.0'
        }
        

Adicionar RecyclerView ao layout

Agora você pode adicionar o RecyclerView ao arquivo de layout. Por exemplo, o layout a seguir usa RecyclerView como a única visualização de todo o layout:

    <?xml version="1.0" encoding="utf-8"?>
    <!-- A RecyclerView with some commonly used attributes -->
    <android.support.v7.widget.RecyclerView
        android:id="@+id/my_recycler_view"
        android:scrollbars="vertical"
        android:layout_width="match_parent"
        android:layout_height="match_parent"/>
    

Depois de adicionar um widget RecyclerView ao layout, acesse um gerenciador para o objeto, conecte-o a um gerenciador de layout e anexe um adaptador para os dados a serem exibidos:

Kotlin

    class MyActivity : Activity() {
        private lateinit var recyclerView: RecyclerView
        private lateinit var viewAdapter: RecyclerView.Adapter<*>
        private lateinit var viewManager: RecyclerView.LayoutManager

        override fun onCreate(savedInstanceState: Bundle?) {
            super.onCreate(savedInstanceState)
            setContentView(R.layout.my_activity)

            viewManager = LinearLayoutManager(this)
            viewAdapter = MyAdapter(myDataset)

            recyclerView = findViewById<RecyclerView>(R.id.my_recycler_view).apply {
                // use this setting to improve performance if you know that changes
                // in content do not change the layout size of the RecyclerView
                setHasFixedSize(true)

                // use a linear layout manager
                layoutManager = viewManager

                // specify an viewAdapter (see also next example)
                adapter = viewAdapter

            }
        }
        // ...
    }
    

Java

    public class MyActivity extends Activity {
        private RecyclerView recyclerView;
        private RecyclerView.Adapter mAdapter;
        private RecyclerView.LayoutManager layoutManager;

        @Override
        protected void onCreate(Bundle savedInstanceState) {
            super.onCreate(savedInstanceState);
            setContentView(R.layout.my_activity);
            recyclerView = (RecyclerView) findViewById(R.id.my_recycler_view);

            // use this setting to improve performance if you know that changes
            // in content do not change the layout size of the RecyclerView
            recyclerView.setHasFixedSize(true);

            // use a linear layout manager
            layoutManager = new LinearLayoutManager(this);
            recyclerView.setLayoutManager(layoutManager);

            // specify an adapter (see also next example)
            mAdapter = new MyAdapter(myDataset);
            recyclerView.setAdapter(mAdapter);
        }
        // ...
    }
    

Adicionar um adaptador de lista

Para alimentar todos os seus dados para a lista, você precisa estender a classe RecyclerView.Adapter. Esse objeto cria visualizações para itens e substitui o conteúdo de algumas das visualizações por novos itens de dados quando o item original não está mais visível.

O exemplo de código a seguir mostra uma implementação simples para um conjunto de dados que consiste em uma matriz de strings exibidas usando widgets TextView.

Kotlin

    class MyAdapter(private val myDataset: Array<String>) :
            RecyclerView.Adapter<MyAdapter.MyViewHolder>() {

        // Provide a reference to the views for each data item
        // Complex data items may need more than one view per item, and
        // you provide access to all the views for a data item in a view holder.
        // Each data item is just a string in this case that is shown in a TextView.
        class MyViewHolder(val textView: TextView) : RecyclerView.ViewHolder(textView)

        // Create new views (invoked by the layout manager)
        override fun onCreateViewHolder(parent: ViewGroup,
                                        viewType: Int): MyAdapter.MyViewHolder {
            // create a new view
            val textView = LayoutInflater.from(parent.context)
                    .inflate(R.layout.my_text_view, parent, false) as TextView
            // set the view's size, margins, paddings and layout parameters
            ...
            return MyViewHolder(textView)
        }

        // Replace the contents of a view (invoked by the layout manager)
        override fun onBindViewHolder(holder: MyViewHolder, position: Int) {
            // - get element from your dataset at this position
            // - replace the contents of the view with that element
            holder.textView.text = myDataset[position]
        }

        // Return the size of your dataset (invoked by the layout manager)
        override fun getItemCount() = myDataset.size
    }
    

Java

    public class MyAdapter extends RecyclerView.Adapter<MyAdapter.MyViewHolder> {
        private String[] mDataset;

        // Provide a reference to the views for each data item
        // Complex data items may need more than one view per item, and
        // you provide access to all the views for a data item in a view holder
        public static class MyViewHolder extends RecyclerView.ViewHolder {
            // each data item is just a string in this case
            public TextView textView;
            public MyViewHolder(TextView v) {
                super(v);
                textView = v;
            }
        }

        // Provide a suitable constructor (depends on the kind of dataset)
        public MyAdapter(String[] myDataset) {
            mDataset = myDataset;
        }

        // Create new views (invoked by the layout manager)
        @Override
        public MyAdapter.MyViewHolder onCreateViewHolder(ViewGroup parent,
                                                       int viewType) {
            // create a new view
            TextView v = (TextView) LayoutInflater.from(parent.getContext())
                    .inflate(R.layout.my_text_view, parent, false);
            ...
            MyViewHolder vh = new MyViewHolder(v);
            return vh;
        }

        // Replace the contents of a view (invoked by the layout manager)
        @Override
        public void onBindViewHolder(MyViewHolder holder, int position) {
            // - get element from your dataset at this position
            // - replace the contents of the view with that element
            holder.textView.setText(mDataset[position]);

        }

        // Return the size of your dataset (invoked by the layout manager)
        @Override
        public int getItemCount() {
            return mDataset.length;
        }
    }
    

O gerenciador de layout chama o método onCreateViewHolder() do adaptador. Esse método precisa construir um RecyclerView.ViewHolder e definir a visualização que ele usa para exibir o conteúdo. O tipo de ViewHolder precisa corresponder ao tipo declarado na assinatura da classe Adapter. Normalmente, ele define a visualização por meio da inflação de um arquivo de layout XML. Como o fixador de visualização ainda não foi atribuído a nenhum dado específico, o método não define o conteúdo da visualização.

Em seguida, o gerenciador de layout vincula o fixador de visualização a seus dados. Ele faz isso chamando o método onBindViewHolder() do adaptador e passando a posição do fixador de visualização em RecyclerView. O método onBindViewHolder() precisa buscar os dados apropriados e usá-los para preencher o layout do fixador de visualização. Por exemplo, se RecyclerView estiver exibindo uma lista de nomes, o método poderá encontrar o nome apropriado na lista e preencher o widget TextView do fixador de visualização.

Se a lista precisar de uma atualização, chame um método de notificação no objeto RecyclerView.Adapter, como notifyItemChanged(). O gerenciador de layout revincula todos os fixadores de visualização afetados, permitindo que seus dados sejam atualizados.

Dica: a classe ListAdapter pode ser útil para você determinar quais itens da lista precisam ser atualizados depois que ela muda.

Personalizar o RecyclerView

Você pode personalizar os objetos RecyclerView para atender às suas necessidades específicas. As classes padrão fornecem todas as funcionalidades que a maioria dos desenvolvedores precisa. E em muitos casos, a única personalização que você precisa fazer é projetar a visualização para cada fixador de visualização e criar o código para atualizar essas visualizações com os dados apropriados. No entanto, se seu app tiver requisitos específicos, você poderá modificar o comportamento padrão de várias maneiras. As seções a seguir descrevem algumas das outras personalizações comuns.

Como modificar o layout

O RecyclerView usa um gerenciador de layout para posicionar os itens individuais na tela e determinar quando reutilizar visualizações de itens que não estão mais visíveis para o usuário. Para reutilizar (ou reciclar) uma visualização, um gerenciador de layout pode solicitar que o adaptador substitua o conteúdo da visualização por um elemento diferente do conjunto de dados. Reciclar visualizações dessa maneira melhora o desempenho, evitando a criação de visualizações desnecessárias ou a realização de pesquisas findViewById() caras. A Biblioteca de Suporte do Android inclui três gerenciadores de layout padrão, e cada um deles oferece muitas opções de personalização:

Se nenhum desses gerenciadores de layout atende às suas necessidades, você pode criar um gerenciador próprio, estendendo a classe abstrata RecyclerView.LayoutManager.

Adicionar animações de itens

Sempre que um item muda, o RecyclerView usa um animador para alterar a aparência dele. Esse animador é um objeto que estende a classe RecyclerView.ItemAnimator abstrata. Por padrão, o RecyclerView usa DefaultItemAnimator para fornecer a animação. Para fornecer animações personalizadas, defina um objeto animador próprio, estendendo RecyclerView.ItemAnimator.

Ativar a seleção de itens de lista

A biblioteca recyclerview-selection permite que os usuários selecionem itens na lista RecyclerView usando a entrada por toque ou mouse. Você mantém o controle sobre a apresentação visual de um item selecionado. Você também pode manter o controle sobre as políticas que controlam o comportamento de seleção, por exemplos, itens que podem ser qualificados para seleção e quantos itens podem ser selecionados.

Para adicionar suporte de seleção a uma instância do RecyclerView, siga estas etapas:

  1. Determine qual tipo de chave de seleção usar e crie um ItemKeyProvider.

    Você pode usar três tipos de chave para identificar itens selecionados: Parcelable (e todas as subclasses, como Uri), String e Long. Para ver informações detalhadas sobre os tipos de chave de seleção, consulte SelectionTracker.Builder.

  2. Implemente ItemDetailsLookup.
  3. ItemDetailsLookup permite que a biblioteca de seleção acesse informações sobre itens RecyclerView que receberam MotionEvent. Ele é efetivamente uma fábrica para instâncias de ItemDetails que são armazenadas em backup por (ou extraídas de) uma instância de RecyclerView.ViewHolder.

  4. Atualize o item Views em RecyclerView para indicar que ele foi selecionou ou desmarcado pelo usuário.

    A biblioteca de seleção não fornece uma decoração visual padrão para os itens selecionados. Você precisa fornecer isso quando implementar onBindViewHolder(). A abordagem recomendada é a seguinte:

  5. Use ActionMode para fornecer ao usuário ferramentas para executar uma ação na seleção.
  6. Registre um SelectionTracker.SelectionObserver para ser notificado quando a seleção mudar. Quando uma seleção for criada pela primeira vez, inicie ActionMode para representar isso para o usuário e forneça ações específicas para a seleção. Por exemplo, você pode adicionar um botão "Excluir" à barra ActionMode e conectar a seta "Voltar" à barra para limpar a seleção. Quando a seleção ficar vazia (se o usuário limpou a seleção pela última vez), não se esqueça de encerrar o modo de ação.

  7. Realizar ações secundárias interpretadas
  8. No final do pipeline de processamento de eventos, a biblioteca pode determinar que o usuário está tentando ativar um item tocando nele ou tentando arrastar e soltar um item ou conjunto de itens selecionados. Reaja a essas interpretações registrando o listener apropriado. Para saber mais, consulte SelectionTracker.Builder.

  9. Montar tudo com SelectionTracker.Builder
  10. O exemplo a seguir mostra como juntar essas partes usando a tecla de seleção Long:

    Kotlin

        var tracker = SelectionTracker.Builder(
            "my-selection-id",
            recyclerView,
            StableIdKeyProvider(recyclerView),
            MyDetailsLookup(recyclerView),
            StorageStrategy.createLongStorage())
                .withOnItemActivatedListener(myItemActivatedListener)
                .build()
        

    Java

        SelectionTracker tracker = new SelectionTracker.Builder<>(
                "my-selection-id",
                recyclerView,
                new StableIdKeyProvider(recyclerView),
                new MyDetailsLookup(recyclerView),
                StorageStrategy.createLongStorage())
                .withOnItemActivatedListener(myItemActivatedListener)
                .build();
        

    Para criar uma instância de SelectionTracker, seu app precisa fornecer o mesmo RecyclerView.Adapter que você usou para inicializar RecyclerView para SelectionTracker.Builder. Por esse motivo, você provavelmente precisará injetar a instância de SelectionTracker, uma vez criada, no seu RecyclerView.Adapter depois que RecyclerView.Adapter for criado. Caso contrário, você não poderá verificar o status selecionado de um item com o método onBindViewHolder().

  11. Inclua seleção nos eventos de ciclo de vida da atividade.
  12. Para preservar o estado de seleção em todos os eventos de ciclo de vida da atividade, seu app precisa chamar os métodos onSaveInstanceState() e onRestoreInstanceState() do rastreador de seleção dos métodos onSaveInstanceState() e onRestoreInstanceState() da atividade, respectivamente. Seu app também precisa fornecer um código de seleção exclusivo para o construtor SelectionTracker.Builder. Esse código é obrigatório, porque uma atividade ou um fragmento pode ter mais de uma lista distinta e selecionável, e todos precisam ser mantidos no estado salvo.

    Outros recursos

    RecyclerView é usado no app de demonstração Sunflower (link em inglês).