تتتبّع مكتبة تسجيل الصفحات حالة طلبات تحميل البيانات المقسّمة على صفحات وتعرضها من خلال الفئة LoadState
.
يمكن لتطبيقك تسجيل مستمع من خلال
PagingDataAdapter
لتلقّي معلومات حول الحالة الحالية وتعديل واجهة المستخدم وفقًا لذلك. يتم توفير هذه الحالات من المحوِّل لأنها متزامنة مع واجهة المستخدم.
هذا يعني أنّ المستمع هو يتلقّى تحديثات عند تحميل الصفحة
على واجهة المستخدم.
يتم توفير إشارة LoadState
منفصلة لكل
LoadType
ونوع مصدر البيانات
(إما PagingSource
أو
RemoteMediator
). ويوفِّر العنصر
CombinedLoadStates
الذي يقدّمه المستمع معلومات عن حالة التحميل
من كل هذه الإشارات. ويمكنك استخدام هذه المعلومات المفصّلة لعرض
مؤشرات التحميل المناسبة للمستخدمين.
حالات التحميل
تعرض "مكتبة تسجيل الصفحات" حالة التحميل للاستخدام في واجهة المستخدم من خلال الكائن 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
، يُرجى الاطّلاع على الوصول إلى معلومات حالة التحميل الإضافية.
عرض حالة التحميل باستخدام محوّل
توفّر "مكتبة تسجيل الصفحات" محوّل قائمة آخر يسمّى 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.
- تحميل البيانات المقسّمة على صفحات وعرضها
- صفحة من الشبكة وقاعدة البيانات
- نظرة عامة على مكتبة نقل الصفحات