تتتبّع مكتبة Paging حالة طلبات التحميل للبيانات المقسَّمة إلى صفحات وتعرضها
من خلال فئة LoadState.
يمكن لتطبيقك تسجيل مستمع مع الـ
PagingDataAdapter لتلقّي معلومات عن الحالة الحالية وتعديل واجهة المستخدم وفقًا لذلك. يتم توفير هذه الحالات من المحوّل لأنّها متزامنة مع واجهة المستخدم.
يعني ذلك أنّ المتتبِّع يتلقّى التعديلات عند تطبيق تحميل صفحة على واجهة المستخدم.
يتم توفير إشارة LoadState منفصلة لكل
LoadType ونوع مصدر البيانات
(إما PagingSource أو
RemoteMediator). يوفّر عنصر
CombinedLoadStates
الذي يقدّمه المتتبِّع معلومات عن حالة التحميل
من جميع هذه الإشارات. يمكنك استخدام هذه المعلومات التفصيلية لعرض مؤشرات التحميل المناسبة للمستخدمين.
حالات التحميل
تعرض مكتبة Paging حالة التحميل لاستخدامها في واجهة المستخدم من خلال عنصر LoadState. تتّخذ عناصر LoadState أحد الأشكال الثلاثة التالية استنادًا إلى حالة التحميل الحالية:
- إذا لم تكن هناك عملية تحميل نشطة ولم يحدث أي خطأ، يكون
LoadStateعنصرLoadState.NotLoading. يتضمّن هذا الفئة الفرعية أيضًا السمةendOfPaginationReachedالتي تشير إلى ما إذا تم الوصول إلى نهاية الترقيم. - إذا كانت هناك عملية تحميل نشطة، يكون
LoadStateعنصرLoadState.Loading. - إذا حدث خطأ، يكون
LoadStateعنصرLoadState.Error.
هناك طريقتان لاستخدام LoadState في واجهة المستخدم: إما باستخدام متتبِّع أو باستخدام
محوّل قائمة خاص لعرض حالة التحميل مباشرةً في
RecyclerView
القائمة.
الوصول إلى حالة التحميل باستخدام مستمع
للحصول على حالة التحميل للاستخدام العام في واجهة المستخدم، استخدِم
loadStateFlow
مصدر البيانات أو طريقة
addLoadStateListener()
التي يوفّرها PagingDataAdapter. تتيح هاتان الآليتان الوصول إلى
عنصر CombinedLoadStatesيتضمّن معلومات عن سلوك LoadState
لكل نوع تحميل.
في المثال التالي، يعرض PagingDataAdapter مكوّنات مختلفة في واجهة المستخدم استنادًا إلى الحالة الحالية لتحميل التحديث:
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، يُرجى الاطّلاع على الوصول إلى معلومات إضافية عن حالة التحميل.
عرض حالة التحميل باستخدام محوّل
توفر مكتبة Paging محول قائمة آخر يسمى
LoadStateAdapter لغرض عرض حالة التحميل مباشرة في القائمة المعروضة للبيانات المقسمة إلى صفحات. يتيح هذا المحوّل الوصول إلى حالة التحميل الحالية للقائمة، والتي يمكنك تمريرها إلى عنصر نائب مخصّص للعرض يعرض المعلومات.
أولاً، أنشِئ فئة عنصر نائب للعرض تحتفظ بمراجع لعرضَي التحميل والخطأ على شاشتك. أنشِئ دالة bind() تقبل LoadState كمعلَمة. يجب أن تبدّل هذه الدالة مدى ظهور العرض استنادًا إلى معلَمة حالة التحميل:
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); } }
بعد ذلك، أنشِئ فئة تنفّذ LoadStateAdapter، وحدِّد الطريقتَين
onCreateViewHolder()
و
onBindViewHolder(). تنشئ هاتان الطريقتان مثيلاً لعنصر نائب العرض المخصّص وتربطان حالة التحميل المرتبطة به.
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); } }
عرض حالة التحميل كعنوان أو تذييل
لعرض تقدُّم التحميل في عنوان وتذييل، استخدِم الـ
withLoadStateHeaderAndFooter()
من عنصر PagingDataAdapter
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));
يمكنك بدلاً من ذلك استخدام
withLoadStateHeader()
أو
withLoadStateFooter()
إذا كنت تريد أن تعرض قائمة RecyclerView حالة التحميل في
العنوان فقط أو في التذييل فقط.
الوصول إلى معلومات إضافية عن حالة التحميل
يوفّر عنصر CombinedLoadStates من PagingDataAdapter معلومات عن حالات التحميل لتنفيذ PagingSource وأيضًا لتنفيذ RemoteMediator، إذا كان هناك تنفيذ.
لتسهيل الأمر، يمكنك استخدام السمات
refresh,
append, و
prepend
من CombinedLoadStates للوصول إلى عنصر LoadState لنوع التحميل المناسب. تشير هذه السمات بشكل عام إلى حالة التحميل من تنفيذ RemoteMediator إذا كان هناك تنفيذ، وإلا فإنّها تحتوي على حالة التحميل المناسبة من تنفيذ PagingSource. لمزيد من المعلومات التفصيلية
عن المنطق الأساسي، يُرجى الاطّلاع على المستندات المرجعية لـ
CombinedLoadStates.
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; });
ومع ذلك، من المهم تذكُّر أنّه من المضمون أن تكون حالات تحميل PagingSource فقط متزامنة مع التعديلات على واجهة المستخدم. بما أنّ السمات refresh وappend وprepend يمكن أن تأخذ حالة التحميل من PagingSource أو RemoteMediator، فليس من المضمون أن تكون متزامنة مع التعديلات على واجهة المستخدم. يمكن أن يؤدي ذلك إلى حدوث مشاكل في واجهة المستخدم حيث يبدو أنّ التحميل قد اكتمل قبل إضافة أي من البيانات الجديدة إلى واجهة المستخدم.
لهذا السبب، تعمل أدوات الوصول المريحة بشكل جيد لعرض حالة التحميل في عنوان أو تذييل، ولكن في حالات الاستخدام الأخرى، قد تحتاج إلى الوصول تحديدًا إلى حالة التحميل من PagingSource أو RemoteMediator. CombinedLoadStates يوفّر
source و
mediator
السمات لهذا الغرض. تعرض كل من هاتين السمتَين عنصر
LoadStates يحتوي على عناصر LoadState لـ PagingSource أو RemoteMediator
على التوالي:
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
بما أنّ عنصر CombinedLoadStates يتيح الوصول إلى جميع التغييرات في حالة التحميل، من المهم فلترة مصدر بيانات حالة التحميل استنادًا إلى أحداث معيّنة. يضمن ذلك تعديل واجهة المستخدم في الوقت المناسب لتجنُّب التقطّع والتعديلات غير الضرورية على واجهة المستخدم.
على سبيل المثال، لنفترض أنّك تريد عرض طريقة عرض فارغة، ولكن بعد اكتمال تحميل البيانات الأولية فقط. تتطلّب حالة الاستخدام هذه التحقّق من بدء تحميل تحديث البيانات، ثم الانتظار حتى تصبح الحالة NotLoading للتأكّد من اكتمال التحديث. عليك فلترة جميع الإشارات باستثناء الإشارات التي تحتاج إليها:
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); } });
ينتظر هذا المثال إلى أن يتم تعديل حالة تحميل التحديث، ولكن يتم تفعيلها فقط عندما تكون الحالة NotLoading. يضمن ذلك اكتمال التحديث عن بُعد بالكامل قبل إجراء أي تعديلات على واجهة المستخدم.
تتيح واجهات برمجة التطبيقات لمصادر البيانات هذا النوع من العمليات. يمكن لتطبيقك تحديد أحداث التحميل التي يحتاج إليها والتعامل مع البيانات الجديدة عند استيفاء المعايير المناسبة.
اقتراحات مخصصة لك
- ملاحظة: يتم عرض نص الرابط عند إيقاف JavaScript
- تحميل البيانات المقسَّمة إلى صفحات وعرضها
- التقسيم إلى صفحات من الشبكة وقاعدة البيانات
- نظرة عامة على مكتبة Paging