1 min read
Pagination
The Poq Pagination SDK is a wrapper around Android jetpack Paging 3. This SDK simplifies the implementation of the recommended Poq pagination solution which only supports pagination over the network and not over a database. The Poq pagination solution is URL based and your backend needs to return the pagination matching the network model NetworkPagination.
Start using the Pagination SDK
The platform module includes the Pagination SDK module, but if you need to include it, add it to your build.gradle:
implementation ("com.poqstudio:pagination:$VERSION_NAME")Koin modules
Poq Pagination SDK dependencies are injected using Koin. The SDK provides the default dependencies definitions via this Koin module:
paginationModule
Overview
PaginationApiServiceis the interface that your ApiService needs to implement which has two methods:getFirstPageandgetPageByUrl.PoqPagingSourceis the implementation ofBasePagingSource, it is a generic implementation ofRxPagingSource.PoqPagingSourceFactoryis the implementation ofBasePagingSourceFactory, used to createPoqPagingSource. It has 3 generics parameters:PaginatedData: the network model of the data that will be paginated.OutPut: the network model whichPaginationApiServicereturns. It needs to implementPaginationResponseand it contains thePaginatedData, theNetworkPaginationand any other data you require outside the pagination.Parameters: the parameters model, such as an id, which will be forwarded to thePaginationApiService.
BasePagingDataAdapteris the adapter responsible for displaying the PagingData.BaseLoadStateAdapter, and its factoryBaseLoadStateAdapterFactory, is the adapter responsible for displaying the loading and error states when fetching new pages.
How to add a new pagination implementation
To implement a pagination using the Poq Pagination SDK you need to:
- Create a network
PaginatedDatamodel that represents the items that are going to be paginated, such asNetworkItem. - Create a network
OutPutmodel implementingPaginationResponse<NetworkItem>, such asNetworkOutput. - Create your API service implementing
PaginationApiService. - Inside your Repository implementation, create the Pager object with the
BasePagingSourcecreated withBasePagingSourceFactory:class Repository(private val basePagingSourceFactory: BasePagingSourceFactory<NetworkItem, NetworkFeature, Parameters>,private val networkToDomainFeatureItemMapper: Mapper<FeatureItem, NetworkFeatureItem>,private val networkToDomainFeatureDataMapper: Mapper<FeatureData, NetworkFeature>,) {fun getFeatureModel(): FeatureModel {val basePagingSource = basePagingSourceFactory.create(mapHeaders(),parameters)return FeatureModel(itemList = Pager(config = PagingConfig(pageSize = 48), // Recommended page sizepagingSourceFactory = { basePagingSource }).observable.map { pagingData ->pagingData.map(networkToDomainFeatureItemMapper::map)},extraData = basePagingSource.result.map(networkToDomainFeatureDataMapper::map),)}} - Inside your ViewModel implementation, get your
FeatureModel, subscribe to the itemList observable usingcachedIn(viewModelScope)and your extraData observable if you have it:getFeatureModel().apply {compositeDisposable.addAll(itemListList.cachedIn(viewModelScope).subscribe{...},extraData.subscribe{...})} - Inside your Activity/Fragment, create a
ConcatAdapterfrom your implementation ofBasePagingDataAdapterandBaseLoadStateAdapter:val adapter = FeaturePagingDataAdapter(...)val loadStateAdapter = get<BaseLoadStateAdapterFactory>().create { adapter.retry() }recyclerView.adapter = adapter.withLoadStateFooter(loadStateAdapter) - To display the empty, loading and error states of the first page:
- Use the provided methods in
BasePagingDataAdapterto attach/detach a CombinedLoadStates listener. Forward theCombinedLoadStatesand a snapshot of the current list to your ViewModel:private var loadStateListener: ((CombinedLoadStates) -> Unit)? = nullfun attachListener(){loadStateListener = { loadStates ->viewModel.submitPaginationData(loadStates,adapter.snapshot().items)}loadStateListener?.let { adapter.attachLoadStateListener(it) }}fun detachListener(){loadStateListener?.let { adapter.detachLoadStateListener(it) }} - In your ViewModel, expose LiveDatas for the different states from the forwarded data:
val isEmptyContent = MutableLiveData<Boolean>()val isLoadingFirstPage = MutableLiveData<Boolean>()val isError = MutableLiveData<Boolean>()fun submitPaginationData(loadStates: CombinedLoadStates,datasetInAdapter: List<FeatureItem>,) {isLoadingFirstPage.postValue(loadStates.refresh is LoadState.Loading)isError.postValue(loadStates.refresh is LoadState.Error)isEmptyContent.postValue(datasetInAdapter.isEmpty())}
- Use the provided methods in