Exibir listas paginadas

Este guia se baseia na Visão geral da biblioteca Paging e descreve como você pode apresentar listas de informações aos usuários na IU do seu app, principalmente quando essas informações mudam.

Conectar a IU ao seu modelo de visualização

Você pode conectar uma instância de LiveData<PagedList> a um PagedListAdapter, conforme mostrado no snippet de código a seguir:

Kotlin

class ConcertActivity : AppCompatActivity() {
    private val adapter = ConcertAdapter()

    // Use the 'by viewModels()' Kotlin property delegate
    // from the activity-ktx artifact
    private val viewModel: ConcertViewModel by viewModels()

    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState);
        viewModel.concerts.observe(this, Observer { adapter.submitList(it) })
    }
}

Java

public class ConcertActivity extends AppCompatActivity {
    private ConcertAdapter adapter = new ConcertAdapter();
    private ConcertViewModel viewModel;

    @Override
    public void onCreate(@Nullable Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        viewModel = new ViewModelProvider(this).get(ConcertViewModel.class);
        viewModel.concertList.observe(this, adapter::submitList);
    }
}

Como as fontes de dados fornecem novas instâncias de PagedList, a atividade envia esses objetos para o adaptador. A implementação PagedListAdapter define como as atualizações são calculadas e processa automaticamente a paginação e a diferenciação da lista. Portanto, seu ViewHolder só precisa ser vinculado a um item específico:

Kotlin

class ConcertAdapter() :
        PagedListAdapter<Concert, ConcertViewHolder>(DIFF_CALLBACK) {
    override fun onBindViewHolder(holder: ConcertViewHolder, position: Int) {
        val concert: Concert? = getItem(position)

        // Note that "concert" is a placeholder if it's null.
        holder.bindTo(concert)
    }

    companion object {
        private val DIFF_CALLBACK = ... // See Implement the diffing callback section.
    }
}

Java

public class ConcertAdapter
        extends PagedListAdapter<Concert, ConcertViewHolder> {
    protected ConcertAdapter() {
        super(DIFF_CALLBACK);
    }

    @Override
    public void onBindViewHolder(@NonNull ConcertViewHolder holder,
            int position) {
        Concert concert = getItem(position);

        // Note that "concert" can be null if it's a placeholder.
        holder.bindTo(concert);
    }

    private static DiffUtil.ItemCallback<Concert> DIFF_CALLBACK
            = ... // See Implement the diffing callback section.
}

O PagedListAdapter processa eventos de carregamento de página usando um objeto PagedList.Callback. À medida que o usuário rola, PagedListAdapter chama PagedList.loadAround() para fornecer dicas ao PagedList sobre quais itens ele deve buscar em DataSource.

Implementar o callback de diferenciação

O exemplo a seguir mostra uma implementação manual de areContentsTheSame(), que compara campos de objeto relevantes.

Kotlin

private val DIFF_CALLBACK = object : DiffUtil.ItemCallback<Concert>() {
    // The ID property identifies when items are the same.
    override fun areItemsTheSame(oldItem: Concert, newItem: Concert) =
            oldItem.id == newItem.id

    // If you use the "==" operator, make sure that the object implements
    // .equals(). Alternatively, write custom data comparison logic here.
    override fun areContentsTheSame(
            oldItem: Concert, newItem: Concert) = oldItem == newItem
}

Java

private static DiffUtil.ItemCallback<Concert> DIFF_CALLBACK =
        new DiffUtil.ItemCallback<Concert>() {

    @Override
    public boolean areItemsTheSame(Concert oldItem, Concert newItem) {
        // The ID property identifies when items are the same.
        return oldItem.getId() == newItem.getId();
    }

    @Override
    public boolean areContentsTheSame(Concert oldItem, Concert newItem) {
        // Don't use the "==" operator here. Either implement and use .equals(),
        // or write custom data comparison logic here.
        return oldItem.equals(newItem);
    }
};

Como o adaptador inclui sua definição de comparação de itens, ele detecta automaticamente mudanças nesses itens quando um novo objeto PagedList é carregado. Como resultado, o adaptador aciona animações de itens eficientes no seu objeto RecyclerView.

Diferenciação usando um tipo de adaptador diferente

Se você optar por não herdar de PagedListAdapter, como nos casos em que é usada uma biblioteca que fornece o próprio adaptador, ainda será possível usar a funcionalidade de diferenciação do adaptador da biblioteca Paging ao trabalhar diretamente com um objeto AsyncPagedListDiffer.

Fornecer marcadores na IU

Nos casos em que você quer que sua interface apresente uma lista antes que o app termine de buscar dados, você pode mostrar marcadores de itens da lista aos usuários. O PagedList processa esse caso apresentando os dados do item da lista como null até que os dados sejam carregados.

Os marcadores têm os seguintes benefícios:

  • Compatibilidade com barras de rolagem: o PagedList fornece o número de itens da lista para o PagedListAdapter. Essas informações permitem que o adaptador desenhe uma barra de rolagem que transmita o tamanho total da lista. Quando novas páginas são carregadas, a barra de rolagem não pula porque o tamanho da lista não muda.
  • Nenhum ícone de carregamento é necessário: como o tamanho da lista já é conhecido, não é necessário avisar os usuários que mais itens estão sendo carregados. Os próprios marcadores transmitem essas informações.

No entanto, antes de acrescentar a compatibilidade com marcadores, lembre-se dos seguintes pré-requisitos:

  • Necessidade de um conjunto de dados contáveis: as instâncias de DataSource da biblioteca de persistência Room podem contar os itens com eficiência. No entanto, caso você esteja usando uma solução de armazenamento local personalizada ou uma arquitetura de dados somente de rede, pode ser caro ou até impossível determinar quantos itens constituem seu conjunto de dados.
  • Necessidade de um adaptador para considerar os itens não carregados: o mecanismo de adaptador ou apresentação usado para preparar a lista para inflação precisa processar itens de lista nulos. Por exemplo, ao vincular dados a um ViewHolder, é necessário fornecer valores padrão para representar dados não carregados.
  • Necessidade de visualização de itens com o mesmo tamanho: se existir a possibilidade de os tamanhos de itens da lista mudarem com base no conteúdo, como em atualizações de redes sociais, o fading cruzado entre os itens não será bom. Nesse caso, recomendamos desativar os marcadores.

Enviar feedback

Envie comentários e ideias usando os recursos abaixo:

Issue tracker
Informe os problemas para que possamos corrigir os bugs.

Outros recursos

Para saber mais sobre a biblioteca Paging, consulte os recursos a seguir.

Exemplos

Codelabs

Vídeos