{% setvar book_path %}/reference/androidx/_book.yaml{% endsetvar %} {% include "_shared/_reference-head-tags.html" %}
public abstract class PagingDataAdapter<T extends Object, VH extends Object>
RecyclerView.Adapter base class for presenting paged data from PagingData
s in a RecyclerView.
This class is a convenience wrapper around AsyncPagingDataDiffer
that implements common default behavior for item counting, and listening to update events.
To present a Pager
, use collectLatest
to observe Pager.flow
and call submitData
whenever a new PagingData
is emitted.
If using RxJava and LiveData extensions on Pager
, use the non-suspending overload of submitData
, which accepts a Lifecycle.
PagingDataAdapter
listens to internal PagingData
loading events as pages
are loaded, and uses DiffUtil on a background thread to compute fine grained updates as updated content in the form of new PagingData objects are received.
State Restoration: To be able to restore RecyclerView state (e.g. scroll position) after a configuration change / application recreate, PagingDataAdapter
calls RecyclerView.Adapter.setStateRestorationPolicy with RecyclerView.Adapter.StateRestorationPolicy.PREVENT upon initialization and waits for the first page to load before allowing state restoration. Any other call to RecyclerView.Adapter.setStateRestorationPolicy by the application will disable this logic and will rely on the user set value.
val USER_COMPARATOR = object : DiffUtil.ItemCallback<User>() { override fun areItemsTheSame(oldItem: User, newItem: User): Boolean = // User ID serves as unique ID oldItem.userId == newItem.userId override fun areContentsTheSame(oldItem: User, newItem: User): Boolean = // Compare full contents (note: Java users should call .equals()) oldItem == newItem } class UserAdapter : PagingDataAdapter<User, UserViewHolder>(USER_COMPARATOR) { override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): UserViewHolder { return UserViewHolder.create(parent) } override fun onBindViewHolder(holder: UserViewHolder, position: Int) { val repoItem = getItem(position) // Note that item may be null, ViewHolder must support binding null item as placeholder holder.bind(repoItem) } }
Public fields |
|
---|---|
final @NonNull Flow<@NonNull CombinedLoadStates> |
A hot |
Public constructors |
|
---|---|
<T extends Object, VH extends Object> PagingDataAdapter( |
Public methods |
|
---|---|
final void |
addLoadStateListener( Add a |
@NonNull <ERROR CLASS> |
|
final long |
getItemId(int position) Note: |
final @NonNull <ERROR CLASS> |
Returns the presented item at the specified position, without notifying Paging of the item access that would normally trigger page loads. |
final void |
refresh() Refresh the data presented by this |
final void |
removeLoadStateListener( Remove a previously registered |
final void |
retry() Retry any failed load requests that would result in a |
final void |
setHasStableIds(boolean hasStableIds) Stable ids are unsupported by |
void |
setStateRestorationPolicy(@NonNull <ERROR CLASS> strategy) |
final @NonNull ItemSnapshotList<@NonNull T> |
snapshot() Returns a new |
final void |
submitData(@NonNull PagingData<@NonNull T> pagingData) Present a |
final void |
submitData( Present a |
final @NonNull <ERROR CLASS> |
withLoadStateFooter(@NonNull LoadStateAdapter<@NonNull ?> footer) Create a ConcatAdapter with the provided |
final @NonNull <ERROR CLASS> |
withLoadStateHeader(@NonNull LoadStateAdapter<@NonNull ?> header) Create a ConcatAdapter with the provided |
final @NonNull <ERROR CLASS> |
withLoadStateHeaderAndFooter( Create a ConcatAdapter with the provided |
@NonNull
public final @NonNull Flow<@NonNull CombinedLoadStates> loadStateFlow
A hot Flow
of CombinedLoadStates
that emits a snapshot whenever the loading state of the current PagingData
changes.
This flow is conflated, so it buffers the last update to CombinedLoadStates
and immediately delivers the current load states on collection.
@NonNull
public final <T extends Object, VH extends Object> PagingDataAdapter(
@NonNull <ERROR CLASS><@NonNull T> diffCallback,
@NonNull CoroutineDispatcher mainDispatcher,
@NonNull CoroutineDispatcher workerDispatcher
)
@NonNull
public final void addLoadStateListener(
@NonNull Function1<@NonNull CombinedLoadStates, Unit> listener
)
Add a CombinedLoadStates
listener to observe the loading state of the current PagingData
.
As new PagingData
generations are submitted and displayed, the listener will be notified to reflect the current CombinedLoadStates
.
val adapter = UserPagingAdapter() adapter.addLoadStateListener { // show a retry button outside the list when refresh hits an error retryButton.isVisible = it.refresh is LoadState.Error // swipeRefreshLayout displays whether refresh is occurring swipeRefreshLayout.isRefreshing = it.refresh is LoadState.Loading // show an empty state over the list when loading initially, before items are loaded emptyState.isVisible = it.refresh is LoadState.Loading && adapter.itemCount == 0 }
Parameters | |
---|---|
@NonNull Function1<@NonNull CombinedLoadStates, Unit> listener |
|
See also | |
---|---|
removeLoadStateListener |
@NonNull
public final long getItemId(int position)
Note: getItemId
is final, because stable IDs are unnecessary and therefore unsupported.
PagingDataAdapter
's async diffing means that efficient change animations are handled for you, without the performance drawbacks of RecyclerView.Adapter.notifyDataSetChanged. Instead, the diffCallback parameter of the PagingDataAdapter
serves the same functionality - informing the adapter and RecyclerView how items are changed and moved.
@NonNull
public final <ERROR CLASS> peek(@IntRange int index)
Returns the presented item at the specified position, without notifying Paging of the item access that would normally trigger page loads.
Parameters | |
---|---|
@IntRange int index |
Index of the presented item to return, including placeholders. |
Returns | |
---|---|
<ERROR CLASS> |
The presented item at position |
@NonNull
public final void refresh()
Refresh the data presented by this PagingDataAdapter
.
refresh
triggers the creation of a new PagingData
with a new instance of PagingSource
to represent an updated snapshot of the backing dataset. If a RemoteMediator
is set, calling refresh
will also trigger a call to RemoteMediator.load
with LoadType
to allow RemoteMediator
to check for updates to the dataset backing PagingSource
.
Note: This API is intended for UI-driven refresh signals, such as swipe-to-refresh. Invalidation due repository-layer signals, such as DB-updates, should instead use PagingSource.invalidate
.
class MyActivity : AppCompatActivity() { private lateinit var binding: MyActivityBinding private val pagingAdapter = UserPagingAdapter() override fun onCreate(savedInstanceState: Bundle?) { super.onCreate(savedInstanceState) binding = MyActivityBinding.inflate(layoutInflater) setContentView(binding.root) binding.recyclerView.adapter = pagingAdapter pagingAdapter.addLoadStateListener { loadStates -> binding.swipeRefreshLayout.isRefreshing = loadStates.refresh is LoadState.Loading } binding.swipeRefreshLayout.setOnRefreshListener { pagingAdapter.refresh() } } }
See also | |
---|---|
invalidate |
@NonNull
public final void removeLoadStateListener(
@NonNull Function1<@NonNull CombinedLoadStates, Unit> listener
)
Remove a previously registered CombinedLoadStates
listener.
Parameters | |
---|---|
@NonNull Function1<@NonNull CombinedLoadStates, Unit> listener |
Previously registered listener. |
See also | |
---|---|
addLoadStateListener |
@NonNull
public final void retry()
Retry any failed load requests that would result in a LoadState.Error
update to this PagingDataAdapter
.
Unlike refresh
, this does not invalidate PagingSource
, it only retries failed loads within the same generation of PagingData
.
LoadState.Error
can be generated from two types of load requests:
@NonNull
public final void setHasStableIds(boolean hasStableIds)
Stable ids are unsupported by PagingDataAdapter
. Calling this method is an error and will result in an UnsupportedOperationException
.
Parameters | |
---|---|
boolean hasStableIds |
Whether items in data set have unique identifiers or not. |
Throws | |
---|---|
kotlin.UnsupportedOperationException |
Always thrown, since this is unsupported by |
@NonNull
public void setStateRestorationPolicy(@NonNull <ERROR CLASS> strategy)
@NonNull
public final ItemSnapshotList<@NonNull T> snapshot()
Returns a new ItemSnapshotList
representing the currently presented items, including any placeholders if they are enabled.
@NonNull
public final void submitData(@NonNull PagingData<@NonNull T> pagingData)
Present a PagingData
until it is invalidated by a call to refresh
or PagingSource.invalidate
.
submitData
should be called on the same CoroutineDispatcher
where updates will be dispatched to UI, typically Dispatchers.Main
(this is done for you if you use lifecycleScope.launch {}
).
This method is typically used when collecting from a Flow
produced by Pager
. For RxJava or LiveData support, use the non-suspending overload of submitData
, which accepts a Lifecycle.
Note: This method suspends while it is actively presenting page loads from a PagingData
, until the PagingData
is invalidated. Although cancellation will propagate to this call automatically, collecting from a Pager.flow
with the intention of presenting the most up-to-date representation of your backing dataset should typically be done using collectLatest
.
import androidx.activity.viewModels class MyFlowActivity : AppCompatActivity() { val pagingAdapter = UserPagingAdapter() override fun onCreate(savedInstanceState: Bundle?) { super.onCreate(savedInstanceState) val viewModel by viewModels<UserListViewModel>() lifecycleScope.launch { viewModel.pagingFlow .collectLatest { pagingData -> // submitData suspends until loading this generation of data stops // so be sure to use collectLatest {} when presenting a Flow<PagingData> pagingAdapter.submitData(pagingData) } } } }
See also | |
---|---|
Pager |
@NonNull
public final void submitData(
@NonNull <ERROR CLASS> lifecycle,
@NonNull PagingData<@NonNull T> pagingData
)
Present a PagingData
until it is either invalidated or another call to submitData
is made.
This method is typically used when observing a RxJava or LiveData stream produced by Pager
. For Flow
support, use the suspending overload of submitData
, which automates cancellation via CoroutineScope
instead of relying of Lifecycle.
import androidx.activity.viewModels class MyLiveDataActivity : AppCompatActivity() { val pagingAdapter = UserPagingAdapter() override fun onCreate(savedInstanceState: Bundle?) { super.onCreate(savedInstanceState) val viewModel by viewModels<UserListViewModel>() viewModel.pagingLiveData.observe(this) { pagingData -> pagingAdapter.submitData(lifecycle, pagingData) } } }
import androidx.activity.viewModels class MyRxJava2Activity : AppCompatActivity() { val pagingAdapter = UserPagingAdapter() val disposable = CompositeDisposable() override fun onCreate(savedInstanceState: Bundle?) { super.onCreate(savedInstanceState) val viewModel by viewModels<UserListViewModel>() viewModel.pagingFlowable .autoDispose(this) // Using AutoDispose to handle subscription lifecycle .subscribe { pagingData -> pagingAdapter.submitData(lifecycle, pagingData) } } }
See also | |
---|---|
submitData |
|
Pager |
@NonNull
public final <ERROR CLASS> withLoadStateFooter(@NonNull LoadStateAdapter<@NonNull ?> footer)
Create a ConcatAdapter with the provided LoadStateAdapter
s displaying the LoadType.APPEND
as a list item at the start of the presented list.
@NonNull
public final <ERROR CLASS> withLoadStateHeader(@NonNull LoadStateAdapter<@NonNull ?> header)
Create a ConcatAdapter with the provided LoadStateAdapter
s displaying the LoadType.PREPEND
as a list item at the end of the presented list.
@NonNull
public final <ERROR CLASS> withLoadStateHeaderAndFooter(
@NonNull LoadStateAdapter<@NonNull ?> header,
@NonNull LoadStateAdapter<@NonNull ?> footer
)
Create a ConcatAdapter with the provided LoadStateAdapter
s displaying the LoadType.PREPEND
and LoadType.APPEND
s as list items at the start and end respectively.