“This is the seventh day of my participation in the First Challenge 2022. For details: First Challenge 2022

Add the dependent

implementation "Androidx. The paging: the paging - runtime: 3.0.0 - beta02"
implementation "Androidx. The paging: the paging - compose: 1.0.0 - alpha08"

Copy the code

The way retrofit2 is used is described here, and I’ll try to make it as simple as possible. The process for Retrofit2 is

Initialize Retrofit to create a globally available Retrofit object, typically a singleton pattern writing request interface to build a repository

❤️Android OkHttp+Retrofit+Rxjava+Hilt implementation of the Network request framework ❤️

Get back to business

The use of Paging3

paging.pager

Primary entry point into Paging; constructor for a reactive stream of PagingData. Translation is the main entry point for paging; Constructor of the reaction flow for paging data.

This is what this guy looks like

public class Pager<Key : Any, Value : Any>
// propagate to the public API with constructor arguments.
@ExperimentalPagingApi constructor(
    config: PagingConfig,
    initialKey: Key? = null, remoteMediator: RemoteMediator<Key, Value>? , pagingSourceFactory: () -> PagingSource<Key, Value> )Copy the code

There are four parameters

PagingConfig

First, PagingConfig has five configurations

PagingConfig parameters The default value
pageSize: Int There is no
prefetchDistance: Int pageSize
enablePlaceholders: Boolean true
initialLoadSize: Int pageSize * 3
maxSize: Int infinite

PagingConfig: Object used to configure the loading behavior within the pager when loading content from a paging source

Defines the number of items loaded from PagingSource at one time.

pageSize

It should be several times the number of items visible on the screen. Note: pageSize is used to inform PagingSource. Load parameters. LoadSize, but not enforced. PagingSource may ignore this value entirely and still return a valid page.

prefetchDistance

PrefetchDistance defaults to = pageSize defines whether paging data can display empty placeholders if the paging source provides them.

PagingData will display an empty placeholder for content that has not been loaded if the following two conditions are met: 1. Its PagingSource can count all unloaded items (to know the number of empty values to display). 2. Enableplaceholder is set to True

enablePlaceholders

Define the load size of the request for the initial load of the PagingSource, usually greater than the pageSize, so that when the data is first loaded, the range of content loaded is large enough to cover a small scrollbar.

Note: initialLoadSize is used to inform PagingSource. Load parameters. LoadSize, but not enforced. PagingSource may ignore this value entirely, but still return a valid initial page

initialLoadSize

Defines the maximum number of items that can be loaded into PagingData before a page is deleted.

maxSize

The maximum?

initialKey

Create a new entity to hold the PagingConfig configuration

data class AppPagingConfig
Copy the code

PagingSource

We use Pager’s config and initialKey

The load and getRefreshKey methods are overridden for further encapsulation

config: AppPagingConfig = AppPagingConfig()
initialKey: K? = null. Pager( config = baseConfig, initialKey = initialKey ) {object : PagingSource<K, V>() {
        override suspend fun load(params: LoadParams<K>): LoadResult<K, V> {
            return loadData.invoke(params)
        }
        override fun getRefreshKey(state: PagingState<K, V>): K? {
            return initialKey
        }
    }
}.flow.cachedIn(viewModelScope)
Copy the code
loadData: suspend (PagingSource.LoadParams<K>) -> PagingSource.LoadResult<K, V>
Copy the code

The logic that makes a network request in loadData returns loadResult. Page on success and loadResult. Error on failure

The complete code

fun <T : Any> ViewModel.simplePager(
    config: AppPagingConfig = AppPagingConfig(),
    callAction: suspend (page: Int) -> BasicBean<ListWrapper<T>>
): Flow<PagingData<T>> {
    return pager(config, 0) {
        valpage = it.key ? :0
        val response = try {
            // The requested data
            HttpResult.Success(callAction.invoke(page))
        } catch (e: Exception) {
            
            HttpResult.Error(e)
        }
        when (response) {
            is HttpResult.Success -> {
                val data = response.result.data
                val hasNotNext = (data!!!!! .datas.size < it.loadSize) && (data.over)
                // loadresult. Page for details, see the previous article
                //data= Requested data content
                PagingSource.LoadResult.Page(
                    data = response.result.data!!!!! .datas, prevKey =if (page - 1 > 0) page - 1 else null,
                    nextKey = if (hasNotNext) null else page + 1)}is HttpResult.Error -> {
                PagingSource.LoadResult.Error(response.exception)
            }
        }
    }
}

fun <K : Any, V : Any> ViewModel.pager(
    config: AppPagingConfig = AppPagingConfig(),
    initialKey: K? = null,
    loadData: suspend (PagingSource.LoadParams<K>) -> PagingSource.LoadResult<K, V>
): Flow<PagingData<V>> {
    val baseConfig = PagingConfig(
        config.pageSize,
        initialLoadSize = config.initialLoadSize,
        prefetchDistance = config.prefetchDistance,
        maxSize = config.maxSize,
        enablePlaceholders = config.enablePlaceholders
    )
    return Pager(
        config = baseConfig,
        initialKey = initialKey
    ) {
        object : PagingSource<K, V>() {
            override suspend fun load(params: LoadParams<K>): LoadResult<K, V> {
                return loadData.invoke(params)
            }

            override fun getRefreshKey(state: PagingState<K, V>): K? {
                return initialKey
            }

        }
    }.flow.cachedIn(viewModelScope)
}
Copy the code
data class AppPagingConfig(
    val pageSize: Int = 20.val initialLoadSize: Int = 20.val prefetchDistance:Int = 1.val maxSize:Int = PagingConfig.MAX_SIZE_UNBOUNDED,
    val enablePlaceholders:Boolean = false
)
Copy the code

Method of use

Configure paging data in the ViewModel to convert data to flow using flow and cachedIn(viewModelScope) to cache data in the ViewModel

private val pager by lazy {
    simplePager {
       //it = number of pages loaded
       // Request data based on page number
        service.getSquareData(it)
    }.cachedIn(viewModelScope)
}

var viewStates by mutableStateOf(ViewState(pagingData = pager))
    private set


data class ViewState(  
    val pagingData: PagingBean
)
typealiasPagingBean = Flow<PagingData< Entity class >>Copy the code

To render the UI

val viewStates = remember { viewModel.viewStates }

val squareData = viewStates.pagingData.collectAsLazyPagingItems()
LazyColumn() {
    itemsIndexed(squareData) { _, item ->
       Text(text = item.data)}}Copy the code

The next post is used with the SwipeRefresh drop-down refresh