Paging 程式庫會追蹤分頁資料的載入要求狀態,並透過 LoadState
類別公開資料。應用程式可透過 PagingDataAdapter
註冊事件監聽器,以便接收目前狀態的相關資訊,並據此更新使用者介面。這些狀態是由轉接程式提供,因為其與使用者介面同步。這表示事件監聽器在使用者介面套用載入網頁時會收到更新。
針對每個 LoadType
和資料來源類型 (PagingSource
或 RemoteMediator
) 會提供獨立的 LoadState
信號。此事件監聽器提供的 CombinedLoadStates
物件會提供所有信號的載入狀態資訊。您可以使用此詳細資訊,向使用者顯示適用的載入指標。
載入狀態
Paging 程式庫會透過 LoadState
物件公開使用者介面中的載入狀態。根據目前的載入狀態,LoadState
物件採用下列其中一種格式:
- 如果沒有運作中的載入作業且沒有錯誤,則
LoadState
是LoadState.NotLoading
物件。這個子類別也包含endOfPaginationReached
屬性,表示是否已觸及分頁終點。 - 如果有執行中的載入作業,則
LoadState
是LoadState.Loading
物件。 - 如果發生錯誤,則
LoadState
是LoadState.Error
物件。
在您的使用者介面中有兩種使用 LoadState
的方法:使用事件監聽器,或使用特殊的清單轉接程式,直接在 RecyclerView
清單顯示載入狀態。
透過事件監聽器存取載入狀態
如要在使用者介面中查看一般用途的載入狀態,請使用 loadStateFlow
串流或由 PagingDataAdapter
提供的 addLoadStateListener()
方法。這些機制提供 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
的清單轉接程式,可讓您在目前顯示的分頁資料清單中直接顯示載入狀態。此轉接程式可存取清單的目前載入狀態,方便您傳遞給顯示資訊的自訂檢視畫面持有人。
首先,建立檢視畫面持有人類別,以持續參考螢幕上的載入和錯誤檢視畫面。建立可接受 LoadState
做為參數的 bind()
函式。此函式應根據載入狀態參數來切換檢視畫面的瀏覽權限:
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); } }
以標頭或頁尾顯示載入狀態
如要在標頭和頁尾顯示載入進度,請透過 PagingDataAdapter
物件呼叫 withLoadStateHeaderAndFooter()
方法:
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));
如果您想 RecyclerView
清單僅在標頭或僅在頁尾會顯示載入狀態,可呼叫 withLoadStateHeader()
或 withLoadStateFooter()
。
存取其他載入狀態資訊
PagingDataAdapter
的 CombinedLoadStates
物件會提供 PagingSource
導入的載入狀態資訊,以及 RemoteMediator
導入的載入狀態資訊 (如果有的話)。
為了方便,您可以使用 CombinedLoadStates
的 refresh
、append
和 prepend
屬性,即可存取適當載入類型的 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
物件,其中分別包含 PagingSource
或 RemoteMediator
的 LoadState
物件:
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
時才會觸發。這能確保在所有使用者介面更新發生前,遠端重新整理作業已全部完成。
Stream API 可讓您執行此類型作業。您的應用程式可以指定所需的載入事件,並在符合適當條件時處理新資料。
為您推薦
- 注意:系統會在 JavaScript 關閉時顯示連結文字
- 載入並顯示分頁資料
- 從網路和資料庫進行分頁
- Paging 程式庫總覽