Yükleme durumlarını yönetme ve sunma

Sayfalandırma kitaplığı, sayfa bölümü uygulanmış veriler için yükleme isteklerinin durumunu izler ve bunu LoadState sınıfı aracılığıyla kullanıma sunar. Uygulamanız, mevcut durum hakkında bilgi almak ve kullanıcı arayüzünü uygun şekilde güncellemek için PagingDataAdapter'a bir işleyici kaydedebilir. Bu durumlar, kullanıcı arayüzü ile senkronize oldukları için adaptörden sağlanır. Bu, sayfa yüklemesi kullanıcı arayüzüne uygulandığında işleyicinizin güncelleme aldığı anlamına gelir.

Her bir LoadType ve veri kaynağı türü (PagingSource veya RemoteMediator) için ayrı bir LoadState sinyali sağlanır. İşleyici tarafından sağlanan CombinedLoadStates nesnesi, tüm bu sinyallerin yükleme durumu hakkında bilgi verir. Bu ayrıntılı bilgileri, kullanıcılarınıza uygun yükleme göstergeleri göstermek için kullanabilirsiniz.

Yükleme durumları

Sayfalama kitaplığı, yükleme durumunu LoadState nesnesi aracılığıyla kullanıcı arayüzünde kullanılmak üzere gösterir. LoadState nesneleri, geçerli yükleme durumuna bağlı olarak üç biçimden birinde olur:

Kullanıcı arayüzünüzde LoadState kullanmanın iki yolu vardır: bir işleyici kullanmak veya yükleme durumunu doğrudan RecyclerView listesinde sunmak için özel bir liste adaptörü kullanmak.

İşleyici ile yükleme durumuna erişme

Kullanıcı arayüzünüzde genel kullanım için yükleme durumunu almak üzere loadStateFlow akışını veya PagingDataAdapter tarafından sağlanan addLoadStateListener() yöntemini kullanın. Bu mekanizmalar, her bir yükleme türünün LoadState davranışı hakkında bilgi içeren bir CombinedLoadStates nesnesine erişim sağlar.

Aşağıdaki örnekte PagingDataAdapter, yenileme yükünün mevcut durumuna bağlı olarak farklı kullanıcı arayüzü bileşenleri görüntüler:

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

CombinedLoadStates hakkında daha fazla bilgi için Ek yükleme durumu bilgilerine erişme konusuna bakın.

Yükleme durumunu bir adaptörle gösterme

Sayfalandırma kitaplığı, yükleme durumunu doğrudan sayfalandırılmış veriler listesinde sunmak amacıyla LoadStateAdapter adlı başka bir liste bağdaştırıcısı sağlar. Bu bağdaştırıcı, listenin geçerli yükleme durumuna erişim sağlar. Bu durumu, bilgileri görüntüleyen özel bir görünüm sahibine iletebilirsiniz.

Öncelikle, yükleme ve hata görünümlerine referansları ekranınızda tutan bir görüntüleme sahibi sınıfı oluşturun. LoadState parametresini parametre olarak kabul eden bir bind() işlevi oluşturun. Bu işlev, görünüm görünürlüğünü yükleme durumu parametresine göre değiştirmelidir:

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

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

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

Ardından, LoadStateAdapter uygulayan bir sınıf oluşturup onCreateViewHolder() ve onBindViewHolder() yöntemlerini tanımlayın. Bu yöntemler özel görünüm sahibinizin bir örneğini oluşturur ve ilişkili yükleme durumunu bağlar.

Kotlin

// Adapter that displays a loading spinner when
// state is 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

// Adapter that displays a loading spinner when
// state is LoadState.Loading, and an error message and retry
// button when state is 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

// Adapter that displays a loading spinner when
// state is LoadState.Loading, and an error message and retry
// button when state is 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);
  }
}

Yükleme ilerleme durumunu bir üst bilgi ve alt bilgide görüntülemek için PagingDataAdapter nesnenizden withLoadStateHeaderAndFooter() yöntemini çağırın:

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

RecyclerView listesinin yükleme durumunu yalnızca üstbilgide veya yalnızca altbilgide göstermesini istiyorsanız bunun yerine withLoadStateHeader() veya withLoadStateFooter() yöntemini çağırabilirsiniz.

Yükleme durumu ile ilgili ek bilgilere erişme

PagingDataAdapter alanındaki CombinedLoadStates nesnesi, PagingSource ve varsa RemoteMediator uygulamanız için yükleme durumları hakkında bilgi sağlar.

Kolaylık sağlaması açısından, CombinedLoadStates ile ilgili refresh, append ve prepend özelliklerini kullanarak uygun yükleme türü için bir LoadState nesnesine erişebilirsiniz. Bu özellikler, varsa genellikle RemoteMediator uygulamasındaki yükleme durumunu temel alır; aksi takdirde PagingSource uygulamasından uygun yükleme durumunu içerir. Temel mantık hakkında daha ayrıntılı bilgi edinmek için CombinedLoadStates referans belgelerine göz atın.

Kotlin

lifecycleScope.launch {
  pagingAdapter.loadStateFlow.collectLatest { loadStates ->
    // Observe refresh load state from RemoteMediator if present, or
    // from PagingSource otherwise.
    refreshLoadState: LoadState = loadStates.refresh
    // Observe prepend load state from RemoteMediator if present, or
    // from PagingSource otherwise.
    prependLoadState: LoadState = loadStates.prepend
    // Observe append load state from RemoteMediator if present, or
    // from PagingSource otherwise.
    appendLoadState: LoadState = loadStates.append
  }
}

Java

pagingAdapter.addLoadStateListener(loadStates -> {
  // Observe refresh load state from RemoteMediator if present, or
  // from PagingSource otherwise.
  LoadState refreshLoadState = loadStates.refresh;
  // Observe prepend load state from RemoteMediator if present, or
  // from PagingSource otherwise.
  LoadState prependLoadState = loadStates.prepend;
  // Observe append load state from RemoteMediator if present, or
  // from PagingSource otherwise.
  LoadState appendLoadState = loadStates.append;
});

Java

pagingAdapter.addLoadStateListener(loadStates -> {
  // Observe refresh load state from RemoteMediator if present, or
  // from PagingSource otherwise.
  LoadState refreshLoadState = loadStates.refresh;
  // Observe prepend load state from RemoteMediator if present, or
  // from PagingSource otherwise.
  LoadState prependLoadState = loadStates.prepend;
  // Observe append load state from RemoteMediator if present, or
  // from PagingSource otherwise.
  LoadState appendLoadState = loadStates.append;
});

Ancak yalnızca PagingSource yükleme durumlarının kullanıcı arayüzü güncellemeleriyle senkronize edilmesinin garanti edildiğini unutmayın. refresh, append ve prepend özellikleri yükleme durumunu PagingSource veya RemoteMediator değerlerinden alabildiği için kullanıcı arayüzü güncellemeleriyle senkronize olacakları garanti edilmez. Bu durum, yeni veriler kullanıcı arayüzüne eklenmeden önce yüklemenin bitmiş gibi göründüğü kullanıcı arayüzü sorunlarına neden olabilir.

Bu nedenle, kolaylık erişimcileri yük durumunu bir üstbilgi veya altbilgide görüntülemek için iyi bir şekilde çalışır, ancak diğer kullanım alanları için yükleme durumuna özel olarak PagingSource veya RemoteMediator üzerinden erişmeniz gerekebilir. CombinedLoadStates, bu amaç için source ve mediator özelliklerini sağlar. Bu özelliklerin her biri, PagingSource veya RemoteMediator için LoadState nesnelerini içeren bir LoadStates nesnesini sırasıyla gösterir:

Kotlin

lifecycleScope.launch {
  pagingAdapter.loadStateFlow.collectLatest { loadStates ->
    // Directly access the RemoteMediator refresh load state.
    mediatorRefreshLoadState: LoadState? = loadStates.mediator.refresh
    // Directly access the RemoteMediator append load state.
    mediatorAppendLoadState: LoadState? = loadStates.mediator.append
    // Directly access the RemoteMediator prepend load state.
    mediatorPrependLoadState: LoadState? = loadStates.mediator.prepend
    // Directly access the PagingSource refresh load state.
    sourceRefreshLoadState: LoadState = loadStates.source.refresh
    // Directly access the PagingSource append load state.
    sourceAppendLoadState: LoadState = loadStates.source.append
    // Directly access the PagingSource prepend load state.
    sourcePrependLoadState: LoadState = loadStates.source.prepend
  }
}

Java

pagingAdapter.addLoadStateListener(loadStates -> {
  // Directly access the RemoteMediator refresh load state.
  LoadState mediatorRefreshLoadState = loadStates.mediator.refresh;
  // Directly access the RemoteMediator append load state.
  LoadState mediatorAppendLoadState = loadStates.mediator.append;
  // Directly access the RemoteMediator prepend load state.
  LoadState mediatorPrependLoadState = loadStates.mediator.prepend;
  // Directly access the PagingSource refresh load state.
  LoadState sourceRefreshLoadState = loadStates.source.refresh;
  // Directly access the PagingSource append load state.
  LoadState sourceAppendLoadState = loadStates.source.append;
  // Directly access the PagingSource prepend load state.
  LoadState sourcePrependLoadState = loadStates.source.prepend;
});

Java

pagingAdapter.addLoadStateListener(loadStates -> {
  // Directly access the RemoteMediator refresh load state.
  LoadState mediatorRefreshLoadState = loadStates.mediator.refresh;
  // Directly access the RemoteMediator append load state.
  LoadState mediatorAppendLoadState = loadStates.mediator.append;
  // Directly access the RemoteMediator prepend load state.
  LoadState mediatorPrependLoadState = loadStates.mediator.prepend;
  // Directly access the PagingSource refresh load state.
  LoadState sourceRefreshLoadState = loadStates.source.refresh;
  // Directly access the PagingSource append load state.
  LoadState sourceAppendLoadState = loadStates.source.append;
  // Directly access the PagingSource prepend load state.
  LoadState sourcePrependLoadState = loadStates.source.prepend;
});

LoadState'teki zincir operatörleri

CombinedLoadStates nesnesi yük durumundaki tüm değişikliklere erişim sağladığından yük durumu akışını belirli etkinliklere göre filtrelemek önemlidir. Bu, kesintilerden ve gereksiz kullanıcı arayüzü güncellemelerinden kaçınmak için kullanıcı arayüzünüzü uygun zamanda güncellemenizi sağlar.

Örneğin, yalnızca ilk veri yüklemesi tamamlandıktan sonra boş bir görünüm görüntülemek istediğinizi varsayalım. Bu kullanım alanı, bir veri yenileme yüklemesinin başladığını doğrulamanızı ve ardından yenilemenin tamamlandığını onaylamak için NotLoading durumunu beklemenizi gerektirir. İhtiyacınız olanlar dışındaki tüm sinyalleri filtrelemeniz gerekir:

Kotlin

lifecycleScope.launchWhenCreated {
  adapter.loadStateFlow
    // Only emit when REFRESH LoadState for RemoteMediator changes.
    .distinctUntilChangedBy { it.refresh }
    // Only react to cases where REFRESH completes, such as NotLoading.
    .filter { it.refresh is LoadState.NotLoading }
    // Scroll to top is synchronous with UI updates, even if remote load was
    // triggered.
    .collect { binding.list.scrollToPosition(0) }
}

Java

PublishSubject<CombinedLoadStates> subject = PublishSubject.create();
Disposable disposable =
  subject.distinctUntilChanged(CombinedLoadStates::getRefresh)
  .filter(
    combinedLoadStates -> combinedLoadStates.getRefresh() instanceof LoadState.NotLoading)
  .subscribe(combinedLoadStates -> binding.list.scrollToPosition(0));

pagingAdapter.addLoadStateListener(loadStates -> {
  subject.onNext(loadStates);
});

Java

LiveData<CombinedLoadStates> liveData = new MutableLiveData<>();
LiveData<LoadState> refreshLiveData =
  Transformations.map(liveData, CombinedLoadStates::getRefresh);
LiveData<LoadState> distinctLiveData =
  Transformations.distinctUntilChanged(refreshLiveData);

distinctLiveData.observeForever(loadState -> {
  if (loadState instanceof LoadState.NotLoading) {
    binding.list.scrollToPosition(0);
  }
});

Bu örnek, yenileme yükleme durumunun güncellenmesini bekler ancak yalnızca durum NotLoading olduğunda tetiklenir. Bu, herhangi bir kullanıcı arayüzü güncellemesi yapılmadan önce uzaktan yenilemenin tamamen bitmesini sağlar.

Stream API'leri bu tür işlemleri mümkün kılar. Uygulamanız, ihtiyaç duyduğu yükleme etkinliklerini belirtebilir ve uygun ölçütler karşılandığında yeni verileri işleyebilir.