ספריית הדפים עוקבת אחרי המצב של בקשות הטעינה לנתונים שנשלחים לדפים וחושפת
אותה דרך הכיתה LoadState
.
האפליקציה שלך יכולה לרשום מאזינים באמצעות
PagingDataAdapter
עד
לקבל מידע על המצב הנוכחי ולעדכן את ממשק המשתמש בהתאם. האלה
המצבים מתקבלים מהמתאם כי הם מסונכרנים עם ממשק המשתמש.
כלומר, ה-listener מקבל עדכונים אחרי שטעינת הדף
שהוחלו על ממשק המשתמש.
יש אות LoadState
נפרד לכל סוג
LoadType
וסוג מקור הנתונים
(PagingSource
או
RemoteMediator
).
CombinedLoadStates
אובייקט שסופק על ידי ה-listener מספק מידע על מצב הטעינה
מכל האותות האלה. אפשר להשתמש במידע המפורט הזה כדי להציג את
מחווני טעינה שמתאימים למשתמשים שלך.
מצבי טעינה
ספריית הדפים חושפת את מצב הטעינה לשימוש בממשק המשתמש באמצעות
אובייקט 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); } });
הדוגמה הזו ממתינה עד שמצב עומס הרענון יופעל, אבל רק יופעל
כשהמדינה (State) היא NotLoading
. כך אפשר לוודא שהרענון של השלט הרחוק הסתיים באופן מלא
הסתיימה לפני שיתרחשו עדכונים בממשק המשתמש.
ממשקי API של סטרימינג מאפשרים פעולה כזו. האפליקציה שלך יכולה לציין את העומס אירועים שדרושים לו, ומטפלים בנתונים החדשים כשמתקיימים הקריטריונים המתאימים.
מומלץ עבורך
- הערה: טקסט הקישור מוצג כאשר JavaScript מושבת
- טעינה והצגה של נתונים בדפים
- דף מהרשת וממסד הנתונים
- סקירה כללית של ספריית הדפים