Jetpeck paging3实践——无限加载网页列表数据

Posted

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了Jetpeck paging3实践——无限加载网页列表数据相关的知识,希望对你有一定的参考价值。

Jetpeck paging3实践(1)——无限加载网页列表数据

一、问题背景

抽空研究一下jetpeck相关库的使用,来看一下paging3,话不多说,一步步完成paging3的demo,直接上代码。

二、实现方案

(1)gradle中添加相应依赖:

dependencies 
    ...
    implementation androidx.paging:paging-runtime:3.0.0-beta01
    implementation com.squareup.retrofit2:retrofit:2.9.0
    implementation com.squareup.retrofit2:converter-gson:2.9.0


(2)首先根据服务器响应的Json格式定义对应的实体类,新建一个Repo类,代码如下所示:

data class Repo(
    @SerializedName("id") val id: Int,
    @SerializedName("name") val name: String,
    @SerializedName("description") val description: String?,
    @SerializedName("stargazers_count") val starCount: Int
)

(3)然后定义一个RepoResponse类,以集合的形式包裹Repo类:

class RepoResponse(
    @SerializedName("items") val items: List<Repo> = emptyList()
)

(4)接下来定义一个GitHubService用于提供网络请求接口,如下所示:

interface GitHubService 

    @GET("search/repositories?sort=stars&q=Android")
    suspend fun searchRepos(@Query("page") page: Int, @Query("per_page") perPage: Int): RepoResponse

    companion object 
        private const val BASE_URL = "https://api.github.com/"

        fun create(): GitHubService 
            return Retrofit.Builder()
                    .baseUrl(BASE_URL)
                    .addConverterFactory(GsonConverterFactory.create())
                    .build()
                    .create(GitHubService::class.java)
        
    


(5)新建一个RepoPagingSource继承自PagingSource,代码如下所示:

class RepoPagingSource(private val gitHubService: GitHubService) : PagingSource<Int, Repo>() 

    override suspend fun load(params: LoadParams<Int>): LoadResult<Int, Repo> 
        return try 
            val page = params.key ?: 1 // set page 1 as default
            val pageSize = params.loadSize
            val repoResponse = gitHubService.searchRepos(page, pageSize)
            val repoItems = repoResponse.items
            val prevKey = if (page > 1) page - 1 else null
            val nextKey = if (repoItems.isNotEmpty()) page + 1 else null
            LoadResult.Page(repoItems, prevKey, nextKey)
         catch (e: Exception) 
            LoadResult.Error(e)
        
    

    override fun getRefreshKey(state: PagingState<Int, Repo>): Int? = null


(6)接下来需要创建一个Repository类。这是MVVM架构的一个重要组件,如下所示:

object Repository 

    private const val PAGE_SIZE = 50

    private val gitHubService = GitHubService.create()

    fun getPagingData(): Flow<PagingData<Repo>> 
        return Pager(
            config = PagingConfig(PAGE_SIZE),
            pagingSourceFactory =  RepoPagingSource(gitHubService) 
        ).flow
    


(7)新建一个MainViewModel类,代码如下所示:

class MainViewModel : ViewModel() 

    fun getPagingData(): Flow<PagingData<Repo>> 
        return Repository.getPagingData().cachedIn(viewModelScope)
    


(8)新建repo_item.xml布局文件,如下所示:

<?xml version="1.0" encoding="utf-8"?>
<LinearLayout
    xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:tools="http://schemas.android.com/tools"
    android:layout_width="match_parent"
    android:layout_height="wrap_content"
    android:padding="10dp"
    android:orientation="vertical">

    <TextView
        android:id="@+id/name_text"
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:gravity="center_vertical"
        android:maxLines="1"
        android:ellipsize="end"
        android:textColor="#5194fd"
        android:textSize="20sp"
        android:textStyle="bold" />

    <TextView
        android:id="@+id/description_text"
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:layout_marginTop="10dp"
        android:maxLines="10"
        android:ellipsize="end" />

    <LinearLayout
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:layout_marginTop="10dp"
        android:gravity="end"
        tools:ignore="UseCompoundDrawables">

        <ImageView
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            android:layout_gravity="center_vertical"
            android:layout_marginEnd="5dp"
            android:src="@drawable/ic_star"
            tools:ignore="ContentDescription" />

        <TextView
            android:id="@+id/star_count_text"
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            android:layout_gravity="center_vertical" />

    </LinearLayout>

</LinearLayout>

(9)接下来定义RecyclerView的适配器,如下所示:

class RepoAdapter : PagingDataAdapter<Repo, RepoAdapter.ViewHolder>(COMPARATOR) 

    companion object 
        private val COMPARATOR = object : DiffUtil.ItemCallback<Repo>() 
            override fun areItemsTheSame(oldItem: Repo, newItem: Repo): Boolean 
                return oldItem.id == newItem.id
            

            override fun areContentsTheSame(oldItem: Repo, newItem: Repo): Boolean 
                return oldItem == newItem
            
        
    

    class ViewHolder(itemView: View) : RecyclerView.ViewHolder(itemView) 
        val name: TextView = itemView.findViewById(R.id.name_text)
        val description: TextView = itemView.findViewById(R.id.description_text)
        val starCount: TextView = itemView.findViewById(R.id.star_count_text)
    

    override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): ViewHolder 
        val view = LayoutInflater.from(parent.context).inflate(R.layout.repo_item, parent, false)
        return ViewHolder(view)
    

    override fun onBindViewHolder(holder: ViewHolder, position: Int) 
        val repo = getItem(position)
        if (repo != null) 
            holder.name.text = repo.name
            holder.description.text = repo.description
            holder.starCount.text = repo.starCount.toString()
        
    


接下来就差最后一步了,让我们把所有的一切都集成到Activity当中。

(10)修改activity_main.xml布局,在里面定义一个RecyclerView和一个ProgressBar,如下所示:

<?xml version="1.0" encoding="utf-8"?>
<FrameLayout xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:tools="http://schemas.android.com/tools"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    tools:context=".MainActivity">

    <androidx.recyclerview.widget.RecyclerView
        android:id="@+id/recycler_view"
        android:layout_width="match_parent"
        android:layout_height="match_parent" />

    <ProgressBar
        android:id="@+id/progress_bar"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:layout_gravity="center" />

</FrameLayout>

(11)然后修改MainActivity中的代码,如下所示:

class MainActivity : AppCompatActivity() 

    private val viewModel by lazy  ViewModelProvider(this).get(MainViewModel::class.java) 

    private val repoAdapter = RepoAdapter()

    override fun onCreate(savedInstanceState: Bundle?) 
        super.onCreate(savedInstanceState)
        setContentView(R.layout.activity_main)
        val recyclerView = findViewById<RecyclerView>(R.id.recycler_view)
        val progressBar = findViewById<ProgressBar>(R.id.progress_bar)
        recyclerView.layoutManager = LinearLayoutManager(this)
        recyclerView.adapter = repoAdapter
        lifecycleScope.launch 
            viewModel.getPagingData().collect  pagingData ->
                repoAdapter.submitData(pagingData)
            
        
        repoAdapter.addLoadStateListener 
            when (it.refresh) 
                is LoadState.NotLoading -> 
                    progressBar.visibility = View.INVISIBLE
                    recyclerView.visibility = View.VISIBLE
                
                is LoadState.Loading -> 
                    progressBar.visibility = View.VISIBLE
                    recyclerView.visibility = View.INVISIBLE
                
                is LoadState.Error -> 
                    val state = it.refresh as LoadState.Error
                    progressBar.visibility = View.INVISIBLE
                    Toast.makeText(this, "Load Error: $state.error.message", Toast.LENGTH_SHORT).show()
                
            
        
    


(12)在正式运行项目之前,别忘了在你的AndroidManifest.xml文件中添加网络权限:

<?xml version="1.0" encoding="utf-8"?>
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
    package="com.example.paging3sample">

    <uses-permission android:name="android.permission.INTERNET" />
    ...

</manifest>

现在运行一下程序,效果如下图所示:

以上是关于Jetpeck paging3实践——无限加载网页列表数据的主要内容,如果未能解决你的问题,请参考以下文章

Android Paging3 Footer踩坑优化

无限滚动加载最佳实践

将 Paging 3 alpha 更新为稳定导致索引问题 Android

Android-利用Jetpack-Compose-+Paging3+swiperefresh实现分页加载,下拉上拉效果

Vue.js 开发实践:实现精巧的无限加载与分页功能

Vue.js 开发实践:实现精巧的无限加载与分页功能