Google berkomitmen untuk mendorong terwujudnya keadilan ras bagi komunitas Kulit Hitam. Lihat caranya.

Memuat dan menampilkan data yang di-page

Library Paging menyediakan kemampuan andal untuk memuat dan menampilkan data yang di-page dari set data yang lebih besar. Panduan ini menunjukkan cara menggunakan Library Paging untuk menyiapkan aliran data yang di-page dari sumber data jaringan dan menampilkannya di RecyclerView.

Menentukan sumber data

Langkah pertama adalah menentukan implementasi PagingSource untuk mengidentifikasi sumber data. Class API PagingSource mencakup metode load(), yang harus Anda ganti untuk menunjukkan cara mengambil data yang di-page dari sumber data yang sesuai.

Gunakan class PagingSource secara langsung untuk menggunakan coroutine Kotlin untuk pemuatan asinkron. Library Paging juga menyediakan class untuk mendukung framework asinkron lainnya:

Pilih jenis kunci dan nilai

PagingSource<Key, Value> memiliki dua jenis parameter: Key dan Value. Kunci menentukan ID yang digunakan untuk memuat data, dan nilainya adalah jenis data itu sendiri. Misalnya, jika Anda memuat halaman objek User dari jaringan dengan meneruskan nomor halaman Int ke Retrofit, Anda harus memilih Int sebagai jenis Key dan User sebagai jenis Value.

Menentukan PagingSource

Contoh berikut menerapkan PagingSource yang memuat halaman item menurut nomor halaman. Jenis Key adalah Int dan jenis Value adalah User.

Kotlin

class ExamplePagingSource(
    val backend: ExampleBackendService,
    val query: String
) : PagingSource<Int, User>() {
  override suspend fun load(
    params: LoadParams<Int>
  ): LoadResult<Int, User> {
    try {
      // Start refresh at page 1 if undefined.
      val nextPageNumber = params.key ?: 1
      val response = backend.searchUsers(query, nextPageNumber)
      return LoadResult.Page(
        data = response.users,
        prevKey = null, // Only paging forward.
        nextKey = response.nextPageNumber
      )
    } catch (e: Exception) {
      // Handle errors in this block and return LoadResult.Error if it is an
      // expected error (such as a network failure).
    }
  }
}

Java

class ExamplePagingSource extends RxPagingSource<Integer, User> {
  @NonNull
  private ExampleBackendService mBackend;
  @NonNull
  private String mQuery;

  ExamplePagingSource(@NonNull ExampleBackendService backend,
    @NonNull String query) {
    mBackend = backend;
    mQuery = query;
  }

  @NotNull
  @Override
  public Single<LoadResult<Integer, User>> loadSingle(
    @NotNull LoadParams<Integer> params) {
    // Start refresh at page 1 if undefined.
    Integer nextPageNumber = params.getKey();
    if (nextPageNumber == null) {
      nextPageNumber = 1;
    }

    return mBackend.searchUsers(mQuery, nextPageNumber)
      .subscribeOn(Schedulers.io())
      .map(this::toLoadResult)
      .onErrorReturn(LoadResult.Error::new);
  }

  private LoadResult<Integer, User> toLoadResult(
    @NonNull SearchUsersResponse response) {
    return new LoadResult.Page<>(
      response.getUsers(),
      null, // Only paging forward.
      response.getNextPageNumber(),
      LoadResult.Page.COUNT_UNDEFINED,
      LoadResult.Page.COUNT_UNDEFINED);
  }
}

Java

class ExamplePagingSource extends ListenableFuturePagingSource<Integer, User> {
  @NonNull
  private ExampleBackendService mBackend;
  @NonNull
  private String mQuery;
  @NonNull
  private Executor mBgExecutor;

  ExamplePagingSource(
    @NonNull ExampleBackendService backend,
    @NonNull String query, @NonNull Executor bgExecutor) {
    mBackend = backend;
    mQuery = query;
    mBgExecutor = bgExecutor;
  }

  @NotNull
  @Override
  public ListenableFuture<LoadResult<Integer, User>> loadFuture(
    @NotNull LoadParams<Integer> params) {
    // Start refresh at page 1 if undefined.
    Integer nextPageNumber = params.getKey();
    if (nextPageNumber == null) {
      nextPageNumber = 1;
    }

    ListenableFuture<LoadResult<Integer, User>> pageFuture = Futures.transform(
      mBackend.searchUsers(mQuery, nextPageNumber),
      this::toLoadResult, mBgExecutor);

    ListenableFuture<LoadResult<Integer, User>> partialLoadResultFuture = Futures.catching(
      pageFuture, HttpException.class,
      LoadResult.Error::new, mBgExecutor);

    return Futures.catching(partialLoadResultFuture,
      IOException.class, LoadResult.Error::new, mBgExecutor);
  }

  private LoadResult<Integer, User> toLoadResult(
    @NonNull SearchUsersResponse response) {
    return new LoadResult.Page<>(
      response.getUsers(),
      null, // Only paging forward.
      response.getNextPageNumber(),
      LoadResult.Page.COUNT_UNDEFINED,
      LoadResult.Page.COUNT_UNDEFINED);
  }
}

Penerapan PagingSource standar akan meneruskan parameter yang diberikan dalam konstruktornya ke metode load() untuk memuat data yang sesuai untuk kueri. Dalam contoh di atas, parameter tersebut adalah:

  • backend: Instance layanan backend yang menyediakan data tersebut.
  • query: Kueri penelusuran yang akan dikirim ke layanan yang ditunjukkan oleh backend.

Objek LoadParams berisi informasi tentang operasi pemuatan yang akan dilakukan. Ini termasuk kunci dan jumlah item yang akan dimuat.

Objek LoadResult berisi hasil operasi pemuatan. LoadResult adalah class tertutup yang mengambil salah satu dari dua metode, bergantung pada apakah panggilan load() berhasil:

  • Jika pemuatan berhasil, tampilkan objek LoadResult.Page.
  • Jika pemuatan gagal, tampilkan objek LoadResult.Error.

Gambar 1 menggambarkan cara fungsi load() dalam contoh ini menerima kunci untuk setiap pemuatan dan menyediakan kunci untuk pemuatan berikutnya.

Pada setiap panggilan pemuatan(), ExamplePagingSource mengambil kunci saat ini
    dan menampilkan kunci berikutnya yang akan dimuat.
Gambar 1. Diagram yang menunjukkan cara load() menggunakan dan mengupdate kunci.

Menangani error

Permintaan untuk memuat data dapat gagal karena sejumlah alasan, terutama saat memuat melalui jaringan. Laporkan error yang terjadi selama pemuatan dengan menampilkan objek LoadResult.Error dari metode load().

Misalnya, Anda dapat menangkap dan melaporkan kesalahan pemuatan di ExamplePagingSource dari contoh sebelumnya dengan menambahkan hal berikut ke metode load():

Kotlin

catch (e: IOException) {
  // IOException for network failures.
  return LoadResult.Error(e)
} catch (e: HttpException) {
  // HttpException for any non-2xx HTTP status codes.
  return LoadResult.Error(e)
}

Java

return backend.searchUsers(searchTerm, nextPageNumber)
  .subscribeOn(Schedulers.io())
  .map(this::toLoadResult)
  .onErrorReturn(LoadResult.Error::new);

Java

ListenableFuture<LoadResult<Integer, User>> pageFuture = Futures.transform(
  backend.searchUsers(query, nextPageNumber), this::toLoadResult,
  bgExecutor);

ListenableFuture<LoadResult<Integer, User>> partialLoadResultFuture = Futures.catching(
  pageFuture, HttpException.class, LoadResult.Error::new,
  bgExecutor);

return Futures.catching(partialLoadResultFuture,
  IOException.class, LoadResult.Error::new, bgExecutor);

Untuk informasi selengkapnya tentang penanganan kesalahan Retrofit, lihat contoh dalam referensi API PagingSource.

PagingSource mengumpulkan dan mengirimkan objek LoadResult.Error ke UI, sehingga Anda dapat menindaklanjutinya. Untuk informasi selengkapnya tentang menampilkan status pemuatan di UI, lihat Menampilkan status pemuatan.

Menyiapkan aliran PagingData

Selanjutnya, Anda memerlukan aliran data yang di-page dari implementasi PagingSource. Biasanya, Anda harus menyiapkan aliran data di ViewModel. Class Pager menyediakan metode yang menampilkan aliran reaktif objek PagingData dari PagingSource. Library Paging mendukung penggunaan beberapa jenis aliran data, termasuk Flow, LiveData, serta jenis Flowable dan Observable dari RxJava.

Saat membuat instance Pager untuk menyiapkan aliran reaktif, Anda harus menyediakan instance dengan objek konfigurasi PagingConfig dan fungsi yang memberi tahu Pager cara mendapatkan instance dari penerapan PagingSource Anda:

Kotlin

val flow = Pager(
  // Configure how data is loaded by passing additional properties to
  // PagingConfig, such as prefetchDistance.
  PagingConfig(pageSize = 20)
) {
  ExamplePagingSource(backend, query)
}.flow
  .cachedIn(viewModelScope)

Java

// CoroutineScope helper provided by the lifecycle-viewmodel-ktx artifact.
CoroutineScope viewModelScope = ViewModelKt.getViewModelScope(viewModel);
Pager<Integer, User> pager = Pager<>(
  new PagingConfig(/* pageSize = */ 20),
  () -> ExamplePagingSource(backend, query));

Flowable<PagingData<User>> flowable = PagingRx.getFlowable(pager);
PagingRx.cachedIn(flowable, viewModelScope);

Java

// CoroutineScope helper provided by the lifecycle-viewmodel-ktx artifact.
CoroutineScope viewModelScope = ViewModelKt.getViewModelScope(viewModel);
Pager<Integer, User> pager = Pager<>(
  new PagingConfig(/* pageSize = */ 20),
  () -> ExamplePagingSource(backend, query));

PagingLiveData.cachedIn(PagingLiveData.getLiveData(pager), viewModelScope);

Operator cachedIn() membuat aliran data dapat dibagikan dan menyimpan data yang dimuat ke cache dengan CoroutineScope yang disediakan. Contoh ini menggunakan viewModelScope yang disediakan oleh artefak Lifecycle lifecycle-viewmodel-ktx.

Objek Pager memanggil metode load() dari objek PagingSource, yang menyediakannya dengan objek LoadParams dan sebaliknya menerima objek LoadResult.

Menentukan adaptor RecyclerView

Anda juga perlu menyiapkan adaptor untuk menerima data ke dalam daftar RecyclerView. Library Paging menyediakan class PagingDataAdapter untuk tujuan ini.

Tentukan class yang memperluas PagingDataAdapter. Dalam contoh tersebut, UserAdapter memperluas PagingDataAdapter guna menyediakan adaptor RecyclerView untuk item daftar jenis User dan menggunakan UserViewHolder sebagai penampung tampilan:

Kotlin

class UserAdapter(diffCallback: DiffUtil.ItemCallback<User>) :
  PagingDataAdapter<User, UserViewHolder>(diffCallback) {
  override fun onCreateViewHolder(
    parent: ViewGroup,
    viewType: Int
  ): UserViewHolder {
    return UserViewHolder(parent)
  }

  override fun onBindViewHolder(holder: UserViewHolder, position: Int) {
    val item = getItem(position)
    // Note that item may be null. ViewHolder must support binding a
    // null item as a placeholder.
    holder.bind(item)
  }
}

Java

class UserAdapter extends PagingDataAdapter<User, UserViewHolder> {
  UserAdapter(@NotNull DiffUtil.ItemCallback<User> diffCallback) {
    super(diffCallback);
  }

  @NonNull
  @Override
  public UserViewHolder onCreateViewHolder(@NonNull ViewGroup parent, int viewType) {
    return new UserViewHolder(parent);
  }

  @Override
  public void onBindViewHolder(@NonNull UserViewHolder holder, int position) {
    User item = getItem(position);
    // Note that item may be null. ViewHolder must support binding a
    // null item as a placeholder.
    holder.bind(item);
  }
}

Java

class UserAdapter extends PagingDataAdapter<User, UserViewHolder> {
  UserAdapter(@NotNull DiffUtil.ItemCallback<User> diffCallback) {
    super(diffCallback);
  }

  @NonNull
  @Override
  public UserViewHolder onCreateViewHolder(@NonNull ViewGroup parent, int viewType) {
    return new UserViewHolder(parent);
  }

  @Override
  public void onBindViewHolder(@NonNull UserViewHolder holder, int position) {
    User item = getItem(position);
    // Note that item may be null. ViewHolder must support binding a
    // null item as a placeholder.
    holder.bind(item);
  }
}

Adaptor Anda juga harus menentukan metode onCreateViewHolder() dan onBindViewHolder() serta menentukan DiffUtil.ItemCallback. Caranya sama seperti biasanya saat menentukan adaptor daftar RecyclerView:

Kotlin

object UserComparator : DiffUtil.ItemCallback<User>() {
  override fun areItemsTheSame(oldItem: User, newItem: User): Boolean {
    // Id is unique.
    return oldItem.id == newItem.id
  }

  override fun areContentsTheSame(oldItem: User, newItem: User): Boolean {
    return oldItem == newItem
  }
}

Java

class UserComparator extends DiffUtil.ItemCallback<User> {
  @Override
  public boolean areItemsTheSame(@NonNull User oldItem,
    @NonNull User newItem) {
    // Id is unique.
    return oldItem.id.equals(newItem.id);
  }

  @Override
  public boolean areContentsTheSame(@NonNull User oldItem,
    @NonNull User newItem) {
    return oldItem.equals(newItem);
  }
}

Java

class UserComparator extends DiffUtil.ItemCallback<User> {
  @Override
  public boolean areItemsTheSame(@NonNull User oldItem,
    @NonNull User newItem) {
    // Id is unique.
    return oldItem.id.equals(newItem.id);
  }

  @Override
  public boolean areContentsTheSame(@NonNull User oldItem,
    @NonNull User newItem) {
    return oldItem.equals(newItem);
  }
}

Menampilkan data yang di-page dalam UI Anda

Setelah Anda menentukan PagingSource, membuat cara bagi aplikasi untuk menghasilkan aliran PagingData, dan menentukan PagingDataAdapter, Anda siap menghubungkan elemen-elemen tersebut dan menampilkan data yang di-page dalam aktivitas.

Lakukan langkah-langkah berikut dalam metode onCreate aktivitas atau onViewCreated fragmen:

  1. Buat instance class PagingDataAdapter.
  2. Teruskan instance PagingDataAdapter ke daftar RecyclerView yang Anda inginkan untuk menampilkan data yang di-page.
  3. Amati aliran PagingData, dan teruskan setiap nilai yang dihasilkan ke metode submitData() adaptor Anda.

Kotlin

val viewModel by viewModels<ExampleViewModel>()

val pagingAdapter = UserAdapter(UserComparator)
val recyclerView = findViewById<RecyclerView>(R.id.recycler_view)
recyclerView.adapter = pagingAdapter

// Activities can use lifecycleScope directly, but Fragments should instead use
// viewLifecycleOwner.lifecycleScope.
lifecycleScope.launch {
  viewModel.flow.collectLatest { pagingData ->
    pagingAdapter.submitData(pagingData)
  }
}

Java

ExampleViewModel viewModel = new ViewModelProvider(this)
  .get(ExampleViewModel.class);

UserAdapter pagingAdapter = new UserAdapter(new UserComparator());
RecyclerView recyclerView = findViewById<RecyclerView>(
  R.id.recycler_view);
recyclerView.adapter = pagingAdapter

viewModel.flowable
  // Using AutoDispose to handle subscription lifecycle.
  // See: https://github.com/uber/AutoDispose
  .to(autoDisposable(AndroidLifecycleScopeProvider.from(this)))
  .subscribe(pagingData -> pagingAdapter.submitData(lifecycle, pagingData));

Java

ExampleViewModel viewModel = new ViewModelProvider(this)
  .get(ExampleViewModel.class);

UserAdapter pagingAdapter = new UserAdapter(new UserComparator());
RecyclerView recyclerView = findViewById<RecyclerView>(
  R.id.recycler_view);
recyclerView.adapter = pagingAdapter

// Activities can use getLifecycle() directly, but Fragments should instead use
// getViewLifecycleOwner().getLifecycle().
viewModel.liveData.observe(this, pagingData ->
  pagingAdapter.submitData(getLifecycle(), pagingData));

Daftar RecyclerView sekarang menampilkan data yang di-page dari sumber data dan otomatis memuat halaman lain jika diperlukan.

Menampilkan status pemuatan

Library Paging menampilkan status pemuatan untuk digunakan di UI melalui objek LoadState. LoadState mengambil satu dari tiga metode, bergantung pada status pemuatan saat ini:

  • Jika tidak ada operasi pemuatan yang aktif dan tidak ada error, LoadState adalah objek LoadState.NotLoading.
  • Jika ada operasi pemuatan yang aktif, maka LoadState adalah objek LoadState.Loading.
  • Jika ada error, maka LoadState adalah objek LoadState.Error.

Ada dua cara untuk menggunakan LoadState di UI Anda: menggunakan pemroses, dan menggunakan adaptor daftar khusus untuk menyajikan status pemuatan langsung dalam daftar RecyclerView.

Menggunakan pemroses untuk mendapatkan status pemuatan

Guna mendapatkan status pemuatan untuk penggunaan umum di UI Anda, PagingDataAdapter menyertakan metode addLoadStateListener().

Kotlin

// Activities can use lifecycleScope directly, but Fragments should instead use
// viewLifecycleOwner.lifecycleScope.
lifecycleScope.launch {
  pagingAdapter.loadStateFlow.collectLatest { loadStates ->
    progressBar.isVisible = loadStates.refresh is LoadState.Loading
    retry.isVisible = loadState.refresh !is LoadState.Loading
    errorMsg.isVisible = loadState.refresh is LoadState.Error
  }
}

Java

pagingAdapter.addLoadStateListener(loadStates -> {
  progressBar.setVisibility(loadStates.refresh instanceof LoadState.Loading
    ? View.VISIBLE : View.GONE);
  retry.setVisibility(loadStates.refresh instanceof LoadState.Loading
    ? View.GONE : View.VISIBLE);
  errorMsg.setVisibility(loadStates.refresh instanceof LoadState.Error
    ? View.VISIBLE : View.GONE);
});

Java

pagingAdapter.addLoadStateListener(loadStates -> {
  progressBar.setVisibility(loadStates.refresh instanceof LoadState.Loading
    ? View.VISIBLE : View.GONE);
  retry.setVisibility(loadStates.refresh instanceof LoadState.Loading
    ? View.GONE : View.VISIBLE);
  errorMsg.setVisibility(loadStates.refresh instanceof LoadState.Error
    ? View.VISIBLE : View.GONE);
});

Menggunakan adaptor untuk menampilkan status pemuatan

Library Paging menyediakan adaptor daftar lain yang disebut LoadStateAdapter untuk menyajikan status pemuatan langsung dalam daftar data yang di-page yang ditampilkan.

Pertama, buat class yang mengimplementasikan LoadStateAdapter, dan tentukan metode onCreateViewHolder() serta onBindViewHolder():

Kotlin

class LoadStateViewHolder(
  parent: ViewGroup,
  retry: () -> Unit
) : RecyclerView.ViewHolder(
  LayoutInflater.from(parent.context)
    .inflate(R.layout.load_state_item, parent, false)
) {
  private val binding = LoadStateItemBinding.bind(itemView)
  private val progressBar: ProgressBar = binding.progressBar
  private val errorMsg: TextView = binding.errorMsg
  private val retry: Button = binding.retryButton
    .also {
      it.setOnClickListener { retry() }
    }

  fun bind(loadState: LoadState) {
    if (loadState is LoadState.Error) {
      errorMsg.text = loadState.error.localizedMessage
    }

    progressBar.isVisible = loadState is LoadState.Loading
    retry.isVisible = loadState is LoadState.Error
    errorMsg.isVisible = loadState is LoadState.Error
  }
}

// Adapter that displays a loading spinner when
// state = LoadState.Loading, and an error message and retry
// button when state is LoadState.Error.
class ExampleLoadStateAdapter(
  private val retry: () -> Unit
) : LoadStateAdapter<LoadStateViewHolder()> {

  override fun onCreateViewHolder(
    parent: ViewGroup,
    loadState: LoadState
  ) = LoadStateViewHolder(parent, retry)

  override fun onBindViewHolder(
    holder: LoadStateViewHolder,
    loadState: LoadState
  ) = holder.bind(loadState)
}

Java

class LoadStateViewHolder extends RecyclerView.ViewHolder {
  private ProgressBar mProgressBar;
  private TextView mErrorMsg;
  private Button mRetry;

  LoadStateViewHolder(
    @NonNull ViewGroup parent,
    @NonNull View.OnClickListener retryCallback) {
    super(LayoutInflater.from(parent.getContext())
      .inflate(R.layout.load_state_item, parent, false));

    LoadStateItemBinding binding = LoadStateItemBinding.bind(itemView);
    mProgressBar = binding.progressBar;
    mErrorMsg = binding.errorMsg;
    mRetry = binding.retryButton;
  }

  public void bind(LoadState loadState) {
    if (loadState instanceof LoadState.Error) {
      LoadState.Error loadStateError = (LoadState.Error) loadState;
      mErrorMsg.setText(loadStateError.getError().getLocalizedMessage());
    }
    mProgressBar.setVisibility(loadState instanceof LoadState.Loading
      ? View.VISIBLE : View.GONE);
    mRetry.setVisibility(loadState instanceof LoadState.Error
      ? View.VISIBLE : View.GONE);
    mErrorMsg.setVisibility(loadState instanceof LoadState.Error
      ? View.VISIBLE : View.GONE);
  }
}

// Adapter that displays a loading spinner when
// state instanceOf LoadState.Loading, and an error message and
// retry button when state instanceof LoadState.Error.
class ExampleLoadStateAdapter extends LoadStateAdapter<LoadStateViewHolder> {
  private View.OnClickListener mRetryCallback;

  ExampleLoadStateAdapter(View.OnClickListener retryCallback) {
    mRetryCallback = retryCallback;
  }

  @NotNull
  @Override
  public LoadStateViewHolder onCreateViewHolder(@NotNull ViewGroup parent,
    @NotNull LoadState loadState) {
    return new LoadStateViewHolder(parent, mRetryCallback);
  }

  @Override
  public void onBindViewHolder(@NotNull LoadStateViewHolder holder,
    @NotNull LoadState loadState) {
    holder.bind(loadState);
  }
}

Java

class LoadStateViewHolder extends RecyclerView.ViewHolder {
  private ProgressBar mProgressBar;
  private TextView mErrorMsg;
  private Button mRetry;

  LoadStateViewHolder(
    @NonNull ViewGroup parent,
    @NonNull View.OnClickListener retryCallback) {
    super(LayoutInflater.from(parent.getContext())
      .inflate(R.layout.load_state_item, parent, false));

    LoadStateItemBinding binding = LoadStateItemBinding.bind(itemView);
    mProgressBar = binding.progressBar;
    mErrorMsg = binding.errorMsg;
    mRetry = binding.retryButton;
  }

  public void bind(LoadState loadState) {
    if (loadState instanceof LoadState.Error) {
      LoadState.Error loadStateError = (LoadState.Error) loadState;
      mErrorMsg.setText(loadStateError.getError().getLocalizedMessage());
    }
    mProgressBar.setVisibility(loadState instanceof LoadState.Loading
      ? View.VISIBLE : View.GONE);
    mRetry.setVisibility(loadState instanceof LoadState.Error
      ? View.VISIBLE : View.GONE);
    mErrorMsg.setVisibility(loadState instanceof LoadState.Error
      ? View.VISIBLE : View.GONE);
  }
}

// Adapter that displays a loading spinner when
// state instanceOf LoadState.Loading, and an error message and
// retry button when state instanceof LoadState.Error.
class ExampleLoadStateAdapter extends LoadStateAdapter<LoadStateViewHolder> {
  private View.OnClickListener mRetryCallback;

  ExampleLoadStateAdapter(View.OnClickListener retryCallback) {
    mRetryCallback = retryCallback;
  }

  @NotNull
  @Override
  public LoadStateViewHolder onCreateViewHolder(@NotNull ViewGroup parent,
    @NotNull LoadState loadState) {
    return new LoadStateViewHolder(parent, mRetryCallback);
  }

  @Override
  public void onBindViewHolder(@NotNull LoadStateViewHolder holder,
    @NotNull LoadState loadState) {
    holder.bind(loadState);
  }
}

Setelah itu, panggil metode withLoadStateHeaderAndFooter() dari objek PagingDataAdapter Anda:

Kotlin

pagingAdapter
  .withLoadStateHeaderAndFooter(
    header = ExampleLoadStateAdapter(adapter::retry),
    footer = ExampleLoadStateAdapter(adapter::retry)
  )

Java

pagingAdapter
  .withLoadStateHeaderAndFooter(
    new ExampleLoadStateAdapter(pagingAdapter::retry),
    new ExampleLoadStateAdapter(pagingAdapter::retry));

Java

pagingAdapter
  .withLoadStateHeaderAndFooter(
    new ExampleLoadStateAdapter(pagingAdapter::retry),
    new ExampleLoadStateAdapter(pagingAdapter::retry));

Sebagai gantinya, Anda dapat memanggil withLoadStateHeader() atau withLoadStateFooter() jika hanya ingin RecyclerView menampilkan status pemuatan di header atau footer.

Referensi lainnya

Untuk mempelajari Library Paging lebih lanjut, lihat referensi tambahan berikut:

Codelab

Contoh