Cómo visualizar listas paginadas

Esta guía se basa en la Descripción general de la biblioteca de Paging y explica cómo puedes presentar listas de información a los usuarios en la IU de tu app, en especial cuando esta información cambia.

Conecta tu IU al modelo de vista

Puedes conectar una instancia de LiveData<PagedList> a un elemento PagedListAdapter, como se muestra en el siguiente fragmento de código:

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 las fuentes de datos proporcionan instancias nuevas de PagedList, la actividad envía estos objetos al adaptador. La implementación de PagedListAdapter define cómo se procesan las actualizaciones y administra automáticamente la paginación y el diffing de listas. Por lo tanto, tu ViewHolder solo necesita vincularse a un elemento proporcionado en particular:

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

El objeto PagedListAdapter administra los eventos de carga de páginas con un objeto PagedList.Callback. A medida que el usuario se desplaza, el PagedListAdapter llama a PagedList.loadAround() para proporcionar sugerencias a la PagedList subyacente en cuanto a los elementos que debe recuperar de DataSource.

Implementa la devolución de llamada de diffing

En el siguiente ejemplo, se muestra una implementación manual de areContentsTheSame() que compara los campos de objetos 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 el adaptador incluye tu definición de elementos de comparación, este detecta automáticamente los cambios en esos elementos cuando se carga un objeto PagedList nuevo. Como resultado, el adaptador activa animaciones de elementos eficaces en el objeto RecyclerView.

Haz diffing usando un tipo de adaptador diferente

Si decides no heredar de PagedListAdapter, por ejemplo, cuando usas una biblioteca que proporciona su propio adaptador, puedes usar la función de diffing del adaptador de la biblioteca de Paging trabajando directamente con un objeto AsyncPagedListDiffer.

Proporciona marcadores de posición en tu IU

En los casos en que desees que la IU muestre una lista antes de que la app haya terminado de recuperar datos, puedes mostrarles elementos de la lista de marcadores de posición a los usuarios. La PagedList controla este caso presentando los datos de elementos de la lista como null hasta que se cargan los datos.

Los marcadores de posición tienen los siguientes beneficios:

  • Compatibilidad con barras de desplazamiento: La PagedList proporciona la cantidad de elementos que contiene la lista al PagedListAdapter. Esta información permite que el adaptador dibuje una barra de desplazamiento que transmite el tamaño original de la lista. A medida que se cargan las páginas nuevas, la barra de desplazamiento no salta porque tu lista no cambia de tamaño.
  • No se necesita ningún ícono giratorio de carga: Debido a que el tamaño de la lista ya es conocido, no es necesario avisarles a los usuarios que se están cargando más elementos. Los marcadores de posición transmiten esa información.

Sin embargo, antes de agregar compatibilidad con marcadores de posición, ten en cuenta las siguientes condiciones previas:

  • Se requiere un conjunto de datos contables: Las instancias de DataSource de la biblioteca de persistencias Room pueden contar sus elementos de manera eficiente. Sin embargo, si usas una solución de almacenamiento local personalizada o una arquitectura de datos solo de red, puede ser costoso o incluso imposible determinar cuántos elementos componen tu conjunto de datos.
  • Se requiere un adaptador para tener en cuenta los elementos descargados: El adaptador o mecanismo de presentación que utilices para preparar la lista para el aumento debe manejar elementos de lista nulos. Por ejemplo, cuando vinculas datos a un ViewHolder, debes proporcionar valores predeterminados para representar los datos descargados.
  • Se requieren vistas de elementos del mismo tamaño: Si los tamaños de los elementos de la lista pueden cambiar en función de su contenido, como las actualizaciones de las redes sociales, el encadenado entre elementos no se verá bien. Sugerimos inhabilitar los marcadores de posición en este caso.

Envía comentarios

Usa estos recursos para compartir tus comentarios y tus ideas con nosotros:

Herramienta de seguimiento de errores
Informa los problemas para que podamos corregir los errores.

Recursos adicionales

Para obtener más información sobre la biblioteca de Paging, consulta los siguientes recursos.

Ejemplos

Codelabs

Videos