Android Compose——Paging3

effect video

brief description

This Demo is completed using Hilt+Retrofit+Paging3, mainly to demonstrate the use of paging3 paging function. The following are the relevant dependencies required by the Demo

 //retrofit
    implementation 'com.squareup.retrofit2:retrofit:2.9.0'
    implementation 'com.squareup.retrofit2:converter-gson:2.9.0'

    //paging
    implementation 'androidx.paging:paging-runtime:3.1.1'
    implementation 'androidx.paging:paging-compose:1.0.0-alpha14'

    //Dagger - Hilt
    implementation("com.google.dagger:hilt-android:2.44")
    kapt("com.google.dagger:hilt-android-compiler:2.44")

    // Compose dependencies
    implementation "androidx.lifecycle:lifecycle-viewmodel-compose:2.5.1"
    implementation "androidx.hilt:hilt-navigation-compose:1.0.0"

    // Coroutines
    implementation 'org.jetbrains.kotlinx:kotlinx-coroutines-core:1.5.1'
    implementation 'org.jetbrains.kotlinx:kotlinx-coroutines-android:1.6.4'

Hilt+Retrofit

access interface

Define the interface that needs to be accessed. This interface is Github api, and suspendthe field is used to prompt subsequent references. This content needs to be used in the coroutine

interface GithubService {
    
    
    @GET("search/repositories?sort=stars&q=Android")
    suspend fun queryGithubAsync(@Query("per_page")number:Int, @Query("page") page:Int):DetailsBean
}

network instance

UseCaseThree examples are provided, and the final example that needs to be referenced externally . The specific Hiltdependency injection will not be explained here. Those who are interested can refer to Hilt dependency injection

@Module
@InstallIn(SingletonComponent::class)
object AppModule {
    
    

    const val BASE_URL:String = "https://api.github.com/"

    @Singleton
    @Provides
    fun providerRetrofit():Retrofit{
    
    
        return Retrofit.Builder()
            .baseUrl(BASE_URL)
            .addConverterFactory(GsonConverterFactory.create())
            .build()
    }

    @Singleton
    @Provides
    fun providerGithubService(retrofit: Retrofit): GithubService {
    
    
        return retrofit.create(GithubService::class.java)
    }

    @Singleton
    @Provides
    fun providerUseCase(service: GithubService):UseCase{
    
    
        return UseCase(GetProjects(service))
    }
}

In the example provided by Hilt, UseCasethe task of accessing the network interface is implemented in

data class UseCase(
    val getProjects: GetProjects
)
class GetProjects(private val service: GithubService) {
    
    
    suspend operator fun invoke(number:Int,page:Int): DetailsBean {
    
    
        return service.queryGithubAsync(number, page)
    }
}

PagingSource

Our main implementation loadmethod; where is pagethe number of current content pages, pageSizethe number of content that needs to be loaded on each page (can be defined externally), repositoryis the obtained network data entity, and previousPageis the previous page. A judgment is made here. If it is the first When there is one page, return null, otherwise, slide to the previous page; nextPagefor the next page, LoadResult.Pageload the required content for paging; LoadResult.Errorexceptions can be caught

class DataPagingSource(private val useCase: UseCase):PagingSource<Int,DetailBean>() {
    
    
    override fun getRefreshKey(state: PagingState<Int, DetailBean>): Int? = null

    override suspend fun load(params: LoadParams<Int>): LoadResult<Int, DetailBean> {
    
    
       return try {
    
    
           val page = params.key ?: 1 //当前页,默认第一页
           val pageSize = params.loadSize //每页数据条数
           val repository = useCase.getProjects(page,pageSize) //获取的数据源
           val repositoryItem = repository.beans //获取的数据列表
           val previousPage = if (page > 1) page - 1 else null //前一页
           val nextPage = if (repositoryItem.isNotEmpty()) page+1 else null //下一页
           Log.d("hiltViewModel","page=$page size=$pageSize")
           LoadResult.Page(repositoryItem,previousPage,nextPage)
       }catch (e:Exception){
    
    
           LoadResult.Error(e)
       }
    }
}

ViewModel

Call the instance constructed by Hilt in the constructor; the getDatamethod is to obtain the paged data, and the return is Flow<PagingData<DetailBean>>the type, where Flow<PagingData<...>>the external is a fixed writing method, and the internal can be defined according to the needs. Then PagingConfigin the configuration, we need to configure pageSizeand initialLoadSize, if the latter is not defined , then the amount of content per page will be pageSizetripled, and then add what we created above PagingSource; finally converted into a stream, and then placed in a coroutine, which caches PagingData so that any downstream collection of this stream will share the same data

@HiltViewModel
class HomeViewModel @Inject constructor(private val useCase: UseCase):ViewModel() {
    
    
    val PAGE_SIZE = 10

    fun getData():Flow<PagingData<DetailBean>>{
    
    
        return Pager(
            config = PagingConfig(pageSize = PAGE_SIZE, initialLoadSize = PAGE_SIZE),
            pagingSourceFactory = {
    
     DataPagingSource(useCase) }
        ).flow.cachedIn(viewModelScope)
    }
}

View

Get the data in the ViewModel

  val datas = viewModel.getData().collectAsLazyPagingItems()

At the same time, if you need to add bottom refresh status bar, data error and other signs, you need to monitor loadState, and the status is divided into five types:

  • refresh: Triggered by loading data for the first time
  • prepend: Triggered by sliding the previous page
  • append: Triggered by sliding the next page
  • source: corresponds to loading in [PagingSource]
  • mediator: corresponding to the loading from [RemoteMediator]
    We mainly use refreshand here append;
    among them, refreshlisten in, if the data is null, then display a full-screen error prompt, here is the first time to load data;
    then, appendlisten in loadingAnd Errortwo states, in which it loadingshows the loading state at the bottom, Errorand displays the error prompt at the bottom in , which is different from refreshthe Errorstate here, because there is data, there is no need to display a full-screen error prompt, just display the error status bar at the bottom of the data list

@Composable
fun GithubList(viewModel: HomeViewModel = hiltViewModel()){
    
    
    val datas = viewModel.getData().collectAsLazyPagingItems()
    LazyColumn(
        verticalArrangement = Arrangement.spacedBy(10.dp),
        modifier = Modifier
            .background(grey)
            .fillMaxSize()
            .padding(10.dp)
    ){
    
    

        when(datas.loadState.refresh){
    
    
            is LoadState.Loading-> {
    
    item {
    
      loading() }}
            is LoadState.Error-> {
    
    
                if (datas.itemCount <= 0){
    
    
                    item{
    
    
                        /**
                         * 全屏显示错误*/
                        failedScreen() {
    
    
                            datas.retry()
                        }
                    }
                }
            }
        }

        itemsIndexed(datas){
    
     _, value ->
            if (value != null){
    
    
                GithubItem(value)
            }else{
    
    
                empty {
    
    
                    datas.retry()
                }
            }
        }

        when(datas.loadState.append){
    
    
            is LoadState.NotLoading-> {
    
    }
            is LoadState.Loading-> {
    
    
                item {
    
    
                    loading()
                }
            }
            is LoadState.Error-> {
    
    
                if (datas.itemCount > 0){
    
    
                    /**
                     * 底部显示加载错误*/
                    item {
    
     failed(){
    
    datas.retry()} }
                }
            }
        }


    }
}

Supongo que te gusta

Origin blog.csdn.net/News53231323/article/details/128678134
Recomendado
Clasificación