ไลบรารีการแบ่งหน้าจะติดตามสถานะของคำขอโหลดสำหรับข้อมูลที่แบ่งหน้าและเปิดเผย
ผ่านชั้นเรียน LoadState
แอปของคุณสามารถลงทะเบียน Listener ด้วย
PagingDataAdapter ถึง
รับข้อมูลเกี่ยวกับสถานะปัจจุบันของ และอัปเดต UI ให้สอดคล้องกัน เหล่านี้
สถานะจะแสดงจากอะแดปเตอร์เนื่องจากซิงโครนัสกับ UI
ซึ่งหมายความว่า Listener ของคุณจะได้รับการอัปเดตเมื่อการโหลดหน้าเว็บ
ที่ใช้กับ UI แล้ว
สัญญาณ LoadState แต่ละรายการมีไว้สำหรับแต่ละสัญญาณ
LoadType และประเภทแหล่งข้อมูล
(PagingSource หรือ
RemoteMediator)
CombinedLoadStates
ออบเจ็กต์ที่ Listener ระบุไว้ให้ข้อมูลเกี่ยวกับสถานะการโหลด
จากสัญญาณทั้งหมดเหล่านี้ คุณสามารถใช้ข้อมูลโดยละเอียดนี้เพื่อแสดง
สัญญาณบอกสถานะการโหลดที่เหมาะสมให้กับผู้ใช้
กำลังโหลดสถานะ
ไลบรารีการแบ่งหน้าจะแสดงสถานะการโหลดเพื่อใช้ใน UI ผ่านทาง
ออบเจ็กต์ LoadState รายการ ออบเจ็กต์ LoadState รายการมี 1 ใน 3 รูปแบบขึ้นอยู่กับ
สถานะการโหลดปัจจุบัน:
- หากไม่มีการดำเนินการโหลดที่ทำงานอยู่และไม่มีข้อผิดพลาด
LoadStateจะเป็นLoadState.NotLoadingออบเจ็กต์ คลาสย่อยนี้ยังรวมถึงendOfPaginationReachedซึ่งระบุว่าถึงส่วนท้ายของหน้าแล้วหรือไม่ - หากมีการโหลดที่ใช้งานอยู่
LoadStateจะเป็นLoadState.Loadingออบเจ็กต์ - หากมีข้อผิดพลาด
LoadStateจะเป็นLoadState.Error
การใช้ LoadState ใน UI มี 2 วิธี ได้แก่ การใช้ Listener หรือการใช้
อะแดปเตอร์รายการพิเศษเพื่อแสดงสถานะการโหลดโดยตรงใน
RecyclerView
รายการ
เข้าถึงสถานะการโหลดด้วย Listener
หากต้องการดูสถานะการโหลดสำหรับการใช้งานทั่วไปใน UI ให้ใช้
loadStateFlow
หรือ
addLoadStateListener()
ที่ได้รับจาก PagingDataAdapter กลไกเหล่านี้ช่วยให้เข้าถึง
ออบเจ็กต์ CombinedLoadStates ที่มีข้อมูลเกี่ยวกับ LoadState
สำหรับการโหลดแต่ละประเภท
ในตัวอย่างต่อไปนี้ PagingDataAdapter แสดง UI ที่แตกต่างกัน
ที่ขึ้นอยู่กับสถานะปัจจุบันของการโหลดการรีเฟรช
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 เท่านั้น
เพื่อให้แน่ใจว่าการอัปเดต UI ทำงานพร้อมกัน เนื่องจาก refresh
พร็อพเพอร์ตี้ append และ prepend อาจรับสถานะการโหลดจาก
PagingSource หรือ RemoteMediator ก็ไม่รับประกันว่าจะเป็น
พร้อมกันพร้อมการอัปเดต UI ซึ่งอาจทำให้เกิดปัญหากับ UI ในตำแหน่งที่การโหลดปรากฏขึ้น
ให้เสร็จก่อนที่จะเพิ่มข้อมูลใหม่ลงใน UI
ด้วยเหตุนี้ ตัวเข้าถึงเพื่ออำนวยความสะดวกจึงทำงานได้ดีในการแสดงโหลด
ในส่วนหัวหรือส่วนท้าย แต่สำหรับกรณีการใช้งานอื่นๆ คุณอาจต้อง
เข้าถึงสถานะการโหลดโดยเฉพาะจาก 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 ให้สิทธิ์เข้าถึงการเปลี่ยนแปลงทั้งหมดใน
สถานะการโหลด สิ่งสำคัญคือต้องกรองสตรีมสถานะการโหลด โดยอิงตาม
กิจกรรม วิธีนี้ช่วยให้มั่นใจว่าคุณอัปเดต UI ในเวลาที่เหมาะสมเพื่อหลีกเลี่ยง
การกระตุกและการอัปเดต UI ที่ไม่จำเป็น
ตัวอย่างเช่น สมมติว่าคุณต้องการแสดงมุมมองว่างเปล่า แต่หลังจาก
การโหลดข้อมูลเริ่มต้นเสร็จสมบูรณ์ กรณีการใช้งานนี้กำหนดให้คุณต้องยืนยันว่า
เริ่มโหลดการรีเฟรชแล้ว จากนั้นรอให้สถานะ 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 การดำเนินการนี้จะช่วยให้มั่นใจว่าการรีเฟรชจากระยะไกลได้
เสร็จก่อนการอัปเดต UI
API ของสตรีมทำให้การดำเนินการประเภทนี้เป็นไปได้ แอปของคุณระบุโหลดได้ เหตุการณ์ที่ต้องการและจัดการข้อมูลใหม่เมื่อตรงกับเกณฑ์ที่เหมาะสม
แนะนำสำหรับคุณ
- หมายเหตุ: ข้อความลิงก์จะแสดงเมื่อ JavaScript ปิดอยู่
- โหลดและแสดงข้อมูลแบบแบ่งหน้า
- หน้าเว็บจากเครือข่ายและฐานข้อมูล
- ภาพรวมของไลบรารีการแบ่งหน้า