Use of Android Jetpack- Paging2.0

 

     Google has been rolling out Jetpack componentization for quite some time. Various components also emerge in endlessly.

     Jetpack also has a lot of things,

    

 

     Do this   Paging today

     The emergence of Paging is used for paging loading of lists. In fact, there are already many mature and efficient open source list loading controls, such as: Smartrefreshlayout and so on. But what Google launched must have its advantages, and of course its limitations.

     Let’s talk about the advantages first. The use of Paging needs to cooperate with controls such as ViewModle and LiveData. The data request senses and binds the life cycle of the page to avoid memory leaks. It is also necessary to bind the DataSource and the Factory of the DataSource, which can load more data without trace and improve the user experience to a certain extent.

 

    The main process is:

     1: Customize PositionalDataSource, the function inside is to request data paging.

     2: Customize DataSource.Factory, bind PositionalDataSource to LiveData

     3: Activity customizes ViewModel, binds PositionalDataSource and Factory, and makes ViewModel perceive data changes 

     4: ViewModel perceives data changes and updates the submitList of PagedListAdapter.

First look at importing those dependencies:

    implementation "androidx.paging:paging-runtime:2.0.0"
    implementation 'androidx.recyclerview:recyclerview:1.1.0'
    implementation 'com.squareup.retrofit2:retrofit:2.9.0'
    implementation "android.arch.lifecycle:extensions:1.1.1"

 

【1】Look at the Activity code first:

class MainActivity : AppCompatActivity() {


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

        val myPagedListAdapter = MyPagedListAdapter()
        recyclerView.layoutManager = LinearLayoutManager(this)
        recyclerView.adapter = myPagedListAdapter

        /**
         *      ViewModel 绑定 Activity 生命周期
         * */
        val myViewModel = ViewModelProviders.of(this).get(MyViewModel::class.java)

        /**
         *      ViewModel 感知数据变化,更新 Adapter 列表
         * */
        myViewModel.getConvertList().observe(this, Observer {
            myPagedListAdapter.submitList(it)
        })
    }

}

 

[2] Look at the Adapter code:

The PagedListAdapter              is used here . In fact, it inherits the RecyclerView.Adapter , so it is not much different from the RecyclerView.Adapter in use , but there is an additional internal need to implement DiffUtil.ItemCallback . The specific principle of DiffUtil.ItemCallback is not mentioned here, and the blogger has not studied it in detail. It is used to compare data. Its internal implementation method can basically be written to death.

class MyPagedListAdapter extends PagedListAdapter<MyDataBean, MyViewHolder> {


    private static DiffUtil.ItemCallback<MyDataBean> DIFF_CALLBACK =
            new DiffUtil.ItemCallback<MyDataBean>() {
                @Override
                public boolean areItemsTheSame(MyDataBean oldConcert, MyDataBean newConcert) {
                    // 比较两个Item数据是否一样的,可以简单用 Bean 的 hashCode来对比
                    return oldConcert.hashCode() == newConcert.hashCode();
                }

                @Override
                public boolean areContentsTheSame(MyDataBean oldConcert,
                                                  MyDataBean newConcert) {
                    // 写法基本上都这样写死即可
                    return oldConcert.equals(newConcert);
                }
            };

    
    public MyPagedListAdapter() {
        // 通过 构造方法 设置 DiffUtil.ItemCallback 
        super(DIFF_CALLBACK);
    }

    @NonNull
    @Override
    public MyViewHolder onCreateViewHolder(@NonNull ViewGroup parent, int viewType) {
        View inflate = LayoutInflater.from(parent.getContext()).inflate(R.layout.item_my, parent, false);
        return new MyViewHolder(inflate);
    }

    @Override
    public void onBindViewHolder(@NonNull MyViewHolder holder, int position) {
        // getItem() 是 PagedListAdapter 内部方法,通过此方法可以获取到对应位置Item 的数据
        holder.bindData(getItem(position));
    }

}
 
 

By the way, also paste ViewHolder and MyDataBean

ViewHolder :

class MyViewHolder(val itemV: View) : RecyclerView.ViewHolder(itemV) {

    fun bindData(data: MyDataBean) {
        itemV.findViewById<TextView>(R.id.tvNum).text = data.position.toString()

        if (data.position % 2 == 0){
            itemV.findViewById<TextView>(R.id.tvNum).setBackgroundColor(itemV.context.resources.getColor(R.color.colorAccent))
        }else{
            itemV.findViewById<TextView>(R.id.tvNum).setBackgroundColor(itemV.context.resources.getColor(R.color.colorPrimaryDark))
        }
    }
}

MyDataBean :

data class MyDataBean(val position: Int)

 

[3] Look at the DataSource code:

PositionalDataSource, the function inside is to request data paging.

Two methods need to be implemented:

loadInitial() : When the page is opened for the first time, this method needs to be called back to obtain data

loadRange():   After the initialization data is available, this method will be called if data needs to be loaded when sliding.

class MyDataSource : PositionalDataSource<MyDataBean>() {

    /**
     *  第一次打开页面,需要回调此方法来获取数据
     * */
    override fun loadInitial(params: LoadInitialParams, callback: LoadInitialCallback<MyDataBean>) {
        // 获取网络数据
        val list = getData(params.requestedStartPosition, params.pageSize)
        /**
         *      这个方法是返回数据,让 绑定ViewModel 感知。 这里要用对方法
         *      @param1  数据列表
         *      @param2  数据为起始位置
         *      @param3  数据列表总长度,这个一定要设置好哦,如果设置了50,
         *               当列表的长度为50时,列表再也无法出发 loadRange() 去加载更多了
         *               如果不知道列表总长度,可以设置 Int 的最大值 999999999
         *               这里设置 10000
         * */
        callback.onResult(list, 0, 10000)
    }

    /**
     *  当有了初始化数据之后,滑动的时候如果需要加载数据的话,会调用此方法。
     * */
    override fun loadRange(params: LoadRangeParams, callback: LoadRangeCallback<MyDataBean>) {
        /**
         *      params.startPosition  列表需要从 startPosition 加载更多
         *      params.loadSize       列表需要从 startPosition 加载长度 为 loadSize的数据
         * */
        val list = getData(params.startPosition, params.loadSize)
        callback.onResult(list)
    }

    /**
     *  模拟网络获取数据
     * */
    private fun getData(startPosition: Int, pageSize: Int): MutableList<MyDataBean> {

        
        Handler(Looper.getMainLooper()).post {
            Toast.makeText(
                MyApplication.instant,
                "加载数据 从 $startPosition 加载到 ${startPosition + pageSize}",
                Toast.LENGTH_SHORT
            ).show()
        }

        val list = mutableListOf<MyDataBean>()
        for (i in startPosition until startPosition + pageSize) {
            list.add(MyDataBean(i))
        }
        return list
    }
}

 

[4] Look at the DataSource.Factory code:

Bind PositionalDataSource to LiveData

class DataFactory: DataSource.Factory<Int, MyDataBean>() {

    private val mSourceLiveData: MutableLiveData<MyDataSource> =
        MutableLiveData<MyDataSource>()

    override fun create(): DataSource<Int, MyDataBean> {
        val myDataSource = MyDataSource()
        mSourceLiveData.postValue(myDataSource)
        return myDataSource
    }

}

 

[5] Finally, look at the ViewModel code:

class MyViewModel : ViewModel() {

    private var mDataSource : DataSource<Int, MyDataBean>
    private var mDataList: LiveData<PagedList<MyDataBean>>

    init {
        // 把 PositionalDataSource 和 Factory 绑定,让 ViewModel 感知数据的变化
        var dataFactory = DataFactory()
        mDataSource = dataFactory.create()
        /**
         *      @param1 dataFactory 设定 dataFactory
         *      @param2 设定每一次加载的长度 
         *              这个和 PositionalDataSource 回调方法 loadSize 一致的
         * */ 
        mDataList = LivePagedListBuilder(dataFactory, 20).build()
    }

    // 暴露方法,让Activity 感知数据变化,去驱动 Adapter更新列表
    fun getConvertList(): LiveData<PagedList<MyDataBean>> {
        return mDataList
    }
}

[5] Finally, look at the Activity code:

class MainActivity : AppCompatActivity() {


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

        val myPagedListAdapter = MyPagedListAdapter()
        recyclerView.layoutManager = LinearLayoutManager(this)
        recyclerView.adapter = myPagedListAdapter

        /**
         *      ViewModel 绑定 Activity 生命周期
         * */
        val myViewModel = ViewModelProviders.of(this).get(MyViewModel::class.java)

        /**
         *      ViewModel 感知数据变化,更新 Adapter 列表
         * */
        myViewModel.getConvertList().observe(this, Observer {
            myPagedListAdapter.submitList(it)
        })
    }

}

Paste the code of activity_main.xml

<?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=".MainActivity">

    <androidx.recyclerview.widget.RecyclerView
        android:id="@+id/recyclerView"
        android:layout_width="0dp"
        android:layout_height="0dp"
        app:layout_constraintLeft_toLeftOf="parent"
        app:layout_constraintRight_toRightOf="parent"
        app:layout_constraintTop_toTopOf="parent"
        app:layout_constraintBottom_toBottomOf="parent">
    </androidx.recyclerview.widget.RecyclerView>

</androidx.constraintlayout.widget.ConstraintLayout>

Run it to see the effect:

It runs successfully with no issues.

 

At the beginning, I said that Paging has disadvantages. In fact, Paging does not have pull-down refresh, only pull-up to load more functions. This is not sufficient for many listing occasions.

But if you only need to pull up and load more, Paging is still recommended, after all, it is provided by Google.

 

There is no problem with the above code test, please leave a message if you have any questions.

 

Code address: https://github.com/LeoLiang23/PagingDemo.git

Guess you like

Origin blog.csdn.net/Leo_Liang_jie/article/details/109473099