# 安卓中实现分页加载方案(二)——paging基于ItemKeyedDataSource的加载方式

时间:2022-09-30 16:57:43

安卓中实现分页加载方案(二)——paging基于ItemKeyedDataSource的加载方式

背景

组里在研究jetpeck库基于kotlin的使用,本次主要是研究paging的使用。

方案分析

paging的使用,基于ItemKeyedDataSource(PagedKeyUserDataSource )的加载方式目前已经是属于不被推荐的方案,不过还是写个总结记录下,会和后续推荐的方案进行对比。

实现方案

1、导入相关依赖

implementation "androidx.paging:paging-runtime-ktx:3.0.1"
testImplementation "androidx.paging:paging-common-ktx:3.0.1"
implementation "androidx.paging:paging-rxjava2-ktx:3.0.1"

2、创建实体类

class Concert {
     var name: String? = null
     var userId = 0
}

3、创建数据集

基于ItemKeyedDataSource的加载方式

class ConcertItemKeyedDataSource : ItemKeyedDataSource<Int, Concert>() {
    private val TAG = "ItemKeyedDataSource";

    var FIRST_PAGE = 1
    val PAGE_SIZE = 20

    /**
     * 获取下一页的起始位置
     */
    override fun getKey(item: Concert): Int {
        return item.userId
    }

    /**
     * 加载分页数据
     */
    override fun loadAfter(params: LoadParams<Int>, callback: LoadCallback<Concert>) {
        Log.d(TAG, "正在请求第${FIRST_PAGE}数据")
        val concertList = mutableListOf<Concert>()
        val startPosition = FIRST_PAGE++ * PAGE_SIZE
        for (i in startPosition..(startPosition + 19)) {
            val concert = Concert()
            concert.name = "jack$i"
            concert.userId = i
            concertList.add(concert)
        }
        callback.onResult(concertList)
    }

    /**
     * 加载前执行方法
     * 只会执行一次
     */
    override fun loadBefore(params: LoadParams<Int>, callback: LoadCallback<Concert>) {
        Log.d(TAG, "请求第${FIRST_PAGE}数据")
    }

    /**
     * 加载第一页数据
     */
    override fun loadInitial(params: LoadInitialParams<Int>, callback: LoadInitialCallback<Concert>) {
        Log.d(TAG, "加载第一页数据")
        val concertList = mutableListOf<Concert>()
        for (i in 0..19) {
            val concert = Concert()
            concert.name = "jack$i"
            concert.userId = i
            concertList.add(concert)
        }
        callback.onResult(concertList)
    }
}

4、生成数据

class ConcertFactory : DataSource.Factory<Int, Concert>() {

    private var sourceLiveData: MutableLiveData<ConcertItemKeyedDataSource> = MutableLiveData()

    override fun create(): DataSource<Int, Concert> {
        val concertDataSource = ConcertItemKeyedDataSource()
        sourceLiveData.postValue(concertDataSource)
        return concertDataSource
    }
}

5、使用ViewModel来处理数据

class ConcertViewModel : ViewModel() {
    var convertList: LiveData<PagedList<Concert>>? = null
    private var concertDataSource: DataSource<Int, Concert>? = null

    init {
        val concertFactory = ConcertFactory()
        concertDataSource = concertFactory.create()
        val config = PagedList.Config.Builder()
                .setEnablePlaceholders(true).setPageSize(20).setPrefetchDistance(3).build()
        convertList = LivePagedListBuilder(concertFactory, config).build()
    }
}

6、使用adapter来显示数据

open class RecyclerViewAdapter: PagedListAdapter<Concert, RecyclerViewAdapter.RecyclerViewHolder>(DIFF_CALLBACK) {

    inner class RecyclerViewHolder(view: View) : RecyclerView.ViewHolder(view) {
        val tv: TextView = view.findViewById(R.id.tv_content)
    }

    override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): RecyclerViewHolder {
        val view = LayoutInflater.from(parent.context).inflate(R.layout.adapter_paging, parent, false)
        return RecyclerViewHolder(view)
    }

    override fun onBindViewHolder(holder: RecyclerViewHolder, position: Int) {
        val concert = getItem(position)
        if (concert != null) {
            holder.tv.text = concert.name
        }
    }

    companion object {
        private val DIFF_CALLBACK: DiffUtil.ItemCallback<Concert> = object : DiffUtil.ItemCallback<Concert>() {
            override fun areItemsTheSame(oldConcert: Concert, newConcert: Concert): Boolean {
                return oldConcert.name == newConcert.name
            }

            override fun areContentsTheSame(oldConcert: Concert, newConcert: Concert): Boolean {
                return oldConcert.equals(newConcert)
            }
        }
    }
}

7、在activity中实现功能

class PagingActivity : AppCompatActivity() {

    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)
        setContentView(R.layout.activity_paging)

        val adapter = RecyclerViewAdapter()
        val viewModel: ConcertViewModel = ViewModelProvider(this).get(ConcertViewModel::class.java)

        concert_list.adapter = adapter
        concert_list.layoutManager = LinearLayoutManager(this)
        viewModel.convertList?.observe(this, Observer {
            adapter.submitList(it)
        })
    }
}

8、activity对应的xml布局文件如下

# 安卓中实现分页加载方案(二)——paging基于ItemKeyedDataSource的加载方式

<?xml version="1.0" encoding="utf-8"?>
<androidx.constraintlayout.widget.ConstraintLayout xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:app="http://schemas.android.com/apk/res-auto"
    xmlns:tools="http://schemas.android.com/tools"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    tools:context=".activity.PagingActivity">
    <androidx.recyclerview.widget.RecyclerView
        android:id="@+id/concert_list"
        android:layout_width="match_parent"
        android:layout_height="match_parent"/>

</androidx.constraintlayout.widget.ConstraintLayout>

9、adapter中对应的item布局如下

# 安卓中实现分页加载方案(二)——paging基于ItemKeyedDataSource的加载方式

<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
    android:layout_width="match_parent"
    android:orientation="vertical"
    android:layout_gravity="center_horizontal"
    android:layout_height="wrap_content">
<TextView
    android:layout_gravity="center_horizontal"
    android:id="@+id/tv_content"
    android:layout_width="wrap_content"
    android:layout_height="wrap_content"/>
</LinearLayout>

demo效果(可无限下滑)

# 安卓中实现分页加载方案(二)——paging基于ItemKeyedDataSource的加载方式