পরিচালনা করুন এবং লোডিং অবস্থা উপস্থাপন করুন

পেজিং লাইব্রেরি পেজড ডেটার জন্য লোড অনুরোধের অবস্থা ট্র্যাক করে এবং LoadState ক্লাসের মাধ্যমে তা প্রকাশ করে। আপনার অ্যাপ বর্তমান অবস্থা সম্পর্কে তথ্য পেতে এবং সেই অনুযায়ী UI আপডেট করতে PagingDataAdapter সাথে একজন শ্রোতাকে নিবন্ধন করতে পারে। এই অবস্থাগুলি অ্যাডাপ্টার থেকে সরবরাহ করা হয়েছে কারণ তারা UI এর সাথে সিঙ্ক্রোনাস। এর মানে হল যে আপনার শ্রোতা আপডেটগুলি পায় যখন পৃষ্ঠা লোড UI এ প্রয়োগ করা হয়।

প্রতিটি LoadType এবং ডেটা সোর্স টাইপের জন্য একটি পৃথক LoadState সংকেত প্রদান করা হয় (হয় PagingSource বা RemoteMediator )। শ্রোতা দ্বারা প্রদত্ত CombinedLoadStates অবজেক্ট এই সমস্ত সংকেত থেকে লোডিং অবস্থা সম্পর্কে তথ্য প্রদান করে। আপনি আপনার ব্যবহারকারীদের উপযুক্ত লোডিং সূচকগুলি প্রদর্শন করতে এই বিস্তারিত তথ্য ব্যবহার করতে পারেন।

লোড হচ্ছে রাজ্য

পেজিং লাইব্রেরি LoadState অবজেক্টের মাধ্যমে UI-তে ব্যবহারের জন্য লোডিং অবস্থা প্রকাশ করে। LoadState অবজেক্টগুলি বর্তমান লোডিং অবস্থার উপর নির্ভর করে তিনটি ফর্মের একটি গ্রহণ করে:

  • যদি কোন সক্রিয় লোড অপারেশন না থাকে এবং কোন ত্রুটি না থাকে, তাহলে LoadState হল একটি LoadState.NotLoading অবজেক্ট। এই সাবক্লাসটিতে endOfPaginationReached প্রপার্টিও রয়েছে, যা নির্দেশ করে যে পেজিনেশন শেষ হয়েছে কিনা।
  • যদি একটি সক্রিয় লোড অপারেশন থাকে, তাহলে LoadState একটি LoadState.Loading অবজেক্ট।
  • যদি একটি ত্রুটি থাকে, তাহলে LoadState একটি LoadState.Error অবজেক্ট।

আপনার UI-তে LoadState ব্যবহার করার দুটি উপায় রয়েছে: একটি শ্রোতা ব্যবহার করে, অথবা RecyclerView তালিকায় সরাসরি লোডিং অবস্থা উপস্থাপন করতে একটি বিশেষ তালিকা অ্যাডাপ্টার ব্যবহার করে৷

শ্রোতার সাথে লোডিং অবস্থা অ্যাক্সেস করুন

আপনার UI-তে সাধারণ ব্যবহারের জন্য লোডিং অবস্থা পেতে, আপনার PagingDataAdapter দ্বারা প্রদত্ত loadStateFlow স্ট্রীম বা addLoadStateListener() পদ্ধতি ব্যবহার করুন। এই প্রক্রিয়াগুলি একটি 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 সম্পর্কে আরও তথ্যের জন্য, অতিরিক্ত লোডিং অবস্থার তথ্য অ্যাক্সেস করুন দেখুন।

একটি অ্যাডাপ্টারের সাথে লোডিং অবস্থা উপস্থাপন করুন

পেজিং লাইব্রেরি LoadStateAdapter অ্যাডাপ্টার নামক আরেকটি তালিকা অ্যাডাপ্টার প্রদান করে যাতে পেজড ডেটার প্রদর্শিত তালিকায় সরাসরি লোডিং অবস্থা উপস্থাপন করা হয়। এই অ্যাডাপ্টারটি তালিকার বর্তমান লোড অবস্থায় অ্যাক্সেস প্রদান করে, যা আপনি একটি কাস্টম ভিউ হোল্ডারের কাছে পাঠাতে পারেন যা তথ্য প্রদর্শন করে।

প্রথমে, একটি ভিউ হোল্ডার ক্লাস তৈরি করুন যা আপনার স্ক্রিনে লোডিং এবং ত্রুটির দৃশ্যের রেফারেন্স রাখে। একটি 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);
  }
}

একটি শিরোনাম এবং একটি ফুটারে লোডিং অগ্রগতি প্রদর্শন করতে, আপনার PagingDataAdapter অবজেক্ট থেকে withLoadStateHeaderAndFooter() পদ্ধতিতে কল করুন:

কোটলিন

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

আপনি পরিবর্তে withLoadStateHeader() অথবা withLoadStateFooter() কল করতে পারেন যদি আপনি RecyclerView তালিকাটি শুধুমাত্র হেডারে বা শুধুমাত্র ফুটারে লোডিং অবস্থা প্রদর্শন করতে চান।

অতিরিক্ত লোডিং অবস্থার তথ্য অ্যাক্সেস করুন

PagingDataAdapter থেকে CombinedLoadStates অবজেক্ট আপনার PagingSource বাস্তবায়নের জন্য লোড স্টেট এবং আপনার RemoteMediator বাস্তবায়নের জন্য তথ্য প্রদান করে, যদি একটি বিদ্যমান থাকে।

সুবিধার জন্য, আপনি উপযুক্ত লোড ধরনের জন্য একটি LoadState অবজেক্ট অ্যাক্সেস করতে CombinedLoadStates থেকে refresh , append , এবং prepend বৈশিষ্ট্যগুলি ব্যবহার করতে পারেন। এই বৈশিষ্ট্যগুলি সাধারণত 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 লোড স্টেটগুলি UI আপডেটের সাথে সিঙ্ক্রোনাস হওয়ার নিশ্চয়তা রয়েছে৷ যেহেতু refresh , append এবং prepend বৈশিষ্ট্যগুলি সম্ভাব্যভাবে PagingSource বা RemoteMediator থেকে লোডের অবস্থা নিতে পারে, সেগুলি UI আপডেটের সাথে সিঙ্ক্রোনাস হওয়ার নিশ্চয়তা নেই৷ এটি UI সমস্যার কারণ হতে পারে যেখানে UI-তে নতুন ডেটা যোগ করার আগে লোডিং শেষ হয়ে গেছে।

এই কারণে, সুবিধার অ্যাক্সেসরগুলি শিরোনাম বা ফুটারে লোড অবস্থা প্রদর্শনের জন্য ভাল কাজ করে, তবে অন্যান্য ব্যবহারের ক্ষেত্রে আপনাকে বিশেষভাবে PagingSource বা RemoteMediator থেকে লোড অবস্থা অ্যাক্সেস করতে হতে পারে। CombinedLoadStates এই উদ্দেশ্যে source এবং mediator বৈশিষ্ট্য প্রদান করে। এই বৈশিষ্ট্যগুলি প্রতিটি একটি LoadStates অবজেক্টকে প্রকাশ করে যাতে যথাক্রমে PagingSource বা RemoteMediator জন্য LoadState অবজেক্ট থাকে:

কোটলিন

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 হয় তখনই ট্রিগার হয়। এটি নিশ্চিত করে যে কোনও UI আপডেট হওয়ার আগে রিমোট রিফ্রেশ সম্পূর্ণভাবে শেষ হয়েছে।

স্ট্রিম API এই ধরনের অপারেশন সম্ভব করে তোলে। আপনার অ্যাপটি প্রয়োজনীয় লোড ইভেন্টগুলি নির্দিষ্ট করতে পারে এবং উপযুক্ত মানদণ্ড পূরণ হলে নতুন ডেটা পরিচালনা করতে পারে৷

{% শব্দার্থে %} {% endverbatim %} {% শব্দার্থে %} {% endverbatim %}