مدیریت و ارائه حالت های بارگذاری

کتابخانه Paging وضعیت درخواست‌های بارگذاری برای داده‌های صفحه‌بندی شده را ردیابی می‌کند و آن‌ها را از طریق کلاس LoadState نمایش می‌دهد. برنامه شما می‌تواند شنونده‌ای را با PagingDataAdapter ثبت کند تا اطلاعاتی در مورد وضعیت فعلی دریافت کند و رابط کاربری را متناسب با آن به‌روزرسانی کند. این حالت ها از آداپتور ارائه می شوند زیرا با رابط کاربری همگام هستند. این به این معنی است که شنونده شما به‌روزرسانی‌ها را زمانی که بارگذاری صفحه در UI اعمال می‌شود، دریافت می‌کند.

یک سیگنال LoadState جداگانه برای هر LoadType و نوع منبع داده (اعم از PagingSource یا RemoteMediator ) ارائه می شود. شی CombinedLoadStates ارائه شده توسط شنونده اطلاعاتی در مورد وضعیت بارگذاری از تمام این سیگنال ها ارائه می دهد. شما می توانید از این اطلاعات دقیق برای نمایش شاخص های بارگذاری مناسب به کاربران خود استفاده کنید.

حالت های بارگیری

کتابخانه Paging وضعیت بارگیری را برای استفاده در UI از طریق شی LoadState نشان می دهد. اشیاء LoadState بسته به وضعیت بارگذاری فعلی یکی از سه شکل زیر را دارند:

  • اگر عملیات بارگذاری فعال و خطا وجود نداشته باشد، LoadState یک شی LoadState.NotLoading است. این زیر کلاس همچنین شامل ویژگی endOfPaginationReached است که نشان می دهد آیا به پایان صفحه بندی رسیده است یا خیر.
  • اگر عملیات بارگذاری فعال وجود داشته باشد، LoadState یک شی LoadState.Loading است.
  • اگر خطایی وجود داشته باشد، LoadState یک شی LoadState.Error است.

دو راه برای استفاده از LoadState در رابط کاربری وجود دارد: استفاده از شنونده، یا استفاده از یک آداپتور لیست ویژه برای ارائه وضعیت بارگیری مستقیماً در لیست RecyclerView .

با شنونده به وضعیت بارگیری دسترسی پیدا کنید

برای دریافت حالت بارگیری برای استفاده عمومی در رابط کاربری خود، از جریان loadStateFlow یا متد addLoadStateListener() که توسط PagingDataAdapter ارائه شده است استفاده کنید. این مکانیسم ها دسترسی به یک شی CombinedLoadStates را فراهم می کنند که شامل اطلاعاتی در مورد رفتار LoadState برای هر نوع بار است.

در مثال زیر، PagingDataAdapter مؤلفه های UI مختلف را بسته به وضعیت فعلی بار تازه سازی نمایش می دهد:

کاتلین

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

جاوا

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

جاوا

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 ، دسترسی به اطلاعات وضعیت بارگذاری اضافی را ببینید.

وضعیت بارگذاری را با یک آداپتور ارائه دهید

کتابخانه Paging آداپتور لیست دیگری به نام LoadStateAdapter را به منظور ارائه وضعیت بارگیری مستقیماً در لیست نمایش داده شده از داده های صفحه شده ارائه می دهد. این آداپتور دسترسی به وضعیت بارگذاری فعلی لیست را فراهم می کند، که می توانید آن را به نگهدارنده نمایش سفارشی که اطلاعات را نمایش می دهد، منتقل کنید.

ابتدا یک کلاس نگهدارنده view ایجاد کنید که ارجاع به نمایش های بارگیری و خطا را روی صفحه شما نگه می دارد. یک bind() bind ایجاد کنید که یک LoadState به عنوان پارامتر بپذیرد. این تابع باید دید نمای را بر اساس پارامتر وضعیت بار تغییر دهد:

کاتلین

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

جاوا

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

جاوا

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

سپس یک کلاس ایجاد کنید که LoadStateAdapter را پیاده سازی کند و متدهای onCreateViewHolder() و onBindViewHolder() را تعریف کنید. این روش‌ها نمونه‌ای از دارنده نمای سفارشی شما را ایجاد می‌کنند و وضعیت بار مرتبط را متصل می‌کنند.

کاتلین

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

جاوا

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

جاوا

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

برای نمایش پیشرفت بارگذاری در یک هدر و یک پاورقی، متد withLoadStateHeaderAndFooter() را از شی PagingDataAdapter خود فراخوانی کنید:

کاتلین

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

جاوا

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

جاوا

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

اگر می‌خواهید فهرست RecyclerView حالت بارگیری را فقط در سرصفحه یا فقط در پاورقی نمایش دهد، می‌توانید با withLoadStateHeader() یا withLoadStateFooter() تماس بگیرید.

به اطلاعات اضافی وضعیت بارگیری دسترسی داشته باشید

شی CombinedLoadStates از PagingDataAdapter اطلاعاتی در مورد وضعیت های بارگذاری برای اجرای PagingSource و همچنین برای اجرای RemoteMediator شما، در صورت وجود، ارائه می دهد.

برای راحتی، می توانید از ویژگی های refresh ، append و prepend از CombinedLoadStates برای دسترسی به یک شی LoadState برای نوع بار مناسب استفاده کنید. این ویژگی‌ها معمولاً در صورت وجود، به حالت بار از اجرای RemoteMediator تعویق می‌افتند. در غیر این صورت، آنها حاوی وضعیت بار مناسب از پیاده سازی PagingSource هستند. برای اطلاعات دقیق تر در مورد منطق زیربنایی، به مستندات مرجع CombinedLoadStates مراجعه کنید.

کاتلین

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

جاوا

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

جاوا

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

با این حال، مهم است که به یاد داشته باشید که تنها حالت‌های بارگیری PagingSource با به‌روزرسانی‌های رابط کاربری همگام هستند. از آنجایی که ویژگی های refresh ، append و prepend به طور بالقوه می توانند وضعیت بارگذاری را از PagingSource یا RemoteMediator بگیرند، تضمینی برای همگام بودن آنها با به روز رسانی های UI وجود ندارد. این می‌تواند باعث ایجاد مشکلاتی در رابط کاربری شود که در آن به نظر می‌رسد بارگیری قبل از اضافه شدن هر یک از داده‌های جدید به UI تمام می‌شود.

به همین دلیل، دسترسی‌های راحت برای نمایش وضعیت بار در سرصفحه یا پاورقی به خوبی کار می‌کنند، اما برای سایر موارد استفاده ممکن است نیاز باشد که به طور خاص به وضعیت بارگذاری از طریق PagingSource یا RemoteMediator دسترسی داشته باشید. CombinedLoadStates ویژگی های source و mediator را برای این منظور فراهم می کند. این ویژگی ها هر کدام یک شی LoadStates را نشان می دهند که به ترتیب حاوی اشیاء LoadState برای PagingSource یا RemoteMediator است:

کاتلین

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

جاوا

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

جاوا

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

از آنجایی که شی CombinedLoadStates دسترسی به تمام تغییرات در وضعیت بار را فراهم می کند، مهم است که جریان وضعیت بار را بر اساس رویدادهای خاص فیلتر کنید. این تضمین می کند که UI خود را در زمان مناسب به روز کنید تا از لکنت و به روز رسانی های غیر ضروری UI جلوگیری کنید.

به عنوان مثال، فرض کنید که می خواهید یک نمای خالی نمایش دهید، اما فقط پس از اتمام بارگذاری اولیه داده ها. این مورد مستلزم این است که تأیید کنید که بارگیری بازخوانی داده شروع شده است، سپس منتظر باشید تا وضعیت NotLoading تأیید کند که تازه‌سازی کامل شده است. شما باید همه سیگنال ها را فیلتر کنید به جز سیگنال هایی که نیاز دارید:

کاتلین

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

جاوا

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

جاوا

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

این مثال تا زمانی که وضعیت بارگیری به‌روزرسانی شود منتظر می‌ماند، اما فقط زمانی فعال می‌شود که حالت NotLoading باشد. این تضمین می‌کند که به‌روزرسانی از راه دور قبل از انجام هر گونه به‌روزرسانی رابط کاربری کاملاً به پایان رسیده است.

Stream API ها این نوع عملیات را ممکن می سازند. برنامه شما می تواند رویدادهای بار مورد نیاز خود را مشخص کند و در صورت رعایت معیارهای مناسب، داده های جدید را مدیریت کند.

{% کلمه به کلمه %} {% آخر کلمه %} {% کلمه به کلمه %} {% آخر کلمه %}