Jetpack series: Paging components to help you solve the painful realization of the paging load

I believe that many small partners in the project combat, often used interface 分页显示,加载更多 and other functions. Need to do targeted development and debugging features for specific, time-consuming and labor-intensive.

Paging using these components work to simplify the part, allowing developers to focus more on the business of implementation. Let's work together to learn to use under Paging components.


First, look at the use of tabs to load and refresh the effect Paging component implementation:

Page Load

Read database page load


Request data

Paging request data network


Here we analyze these two examples for use Paging components.

  • Database read tab loading example, one-time data acquisition, the interface page display, data loaded on demand, to reduce the use of memory resources
  • The network side paging request data, each fixed length data information requested for display, to reduce network bandwidth

Paging achieve functional components used in the Room, Room is part of Jetpack library provides an abstraction layer on SQLite, SQLite database provides smooth access experience for developers.

Room Introduction

Room assembly comprises three main components:

  • database

    It should meet four conditions:

    1. Notes contain @Database
    2. It is an abstract class that inherits from RoomDatabase
    3. Notes contain information in the list of entities
    4. Comprising a non-parametric method returns class annotated with @Dao
  • Data entities

    It represents the database tables

  • DAO

    It contains methods for accessing databases

Room obtain application uses components associated with the database data access object or DAO, then get entity, all changes will be synchronized to the database entities. The relationship between the three portions Room below:

Chart Room

Room Chart (quoted from official documents)

The basic use Paging

Paging component supports three different data structures:

  • Only network acquisition
  • Equipment acquired only from the database
  • A combination of two sources of data, using the device as a cache database

Paging support data architecture

Paging library supports data architecture (quoted from official documents)

Here we have a database from the device only way to get to know the basic use under Paging paging.

Environment Configuration

First, we need to add in the corresponding library support module build.gradle in.

dependencies {
    versions.room = "2.1.0-alpha06"
    versions.lifecycle = "2.2.0-alpha03"
    versions.paging = "2.1.0-rc01"
    //room数据库访问依赖
    implementation "androidx.room:room-runtime:$versions.room"
    //lifecycle组件依赖,ViewModel
    implementation "androidx.lifecycle:lifecycle-runtime:$versions.lifecycle"
    //paging组件依赖
    implementation "androidx.paging:paging-runtime-ktx:$versions.paging"
    
    kapt "androidx.room:room-compiler:$versions.room"
}
复制代码

Layout file

Layout of the interface is relatively simple, the main interface includes an input field, a button and a RecyclerView, using a list display of each card layout, display text.

<androidx.cardview.widget.CardView ...>
    <TextView android:id="@+id/name" .../>
</androidx.cardview.widget.CardView>
复制代码

data preparation

Before data acquisition and display in the main Activity, you need to make a few preparations:

  1. Create a data entity classes Cheese
  2. Create a database approach DAO
  3. Create a database CheeseDb
  4. Create a custom CheeseViewModel

1. Create an entity Cheese

Each entity represents a database of data objects, note must be added @Entity comment

@Entity
data class Cheese(@PrimaryKey(autoGenerate = true) val id: Int, val name: String)
复制代码

This statement creates a database entity, the fields have ID and Name, the primary key for the ID

2. Create a database operation method DAO

Database methods provide the basic operation of the database, you must add @Dao comment

@Dao
interface CheeseDao {
    @Query("SELECT * FROM Cheese ORDER BY name COLLATE NOCASE ASC")
    fun allCheesesByName(): DataSource.Factory<Int, Cheese>
    @Insert
    fun insert(cheeses: List<Cheese>)
    @Insert
    fun insert(cheese: Cheese)
    @Delete
    fun delete(cheese: Cheese)
}
复制代码

Here provides for the query, insert, and delete database method, you can specify the data source type to see inside the query method, currently used by the default type. Paging also support three data sources:

  • PageKeyedDataSource

    Press up and down to achieve page load display

  • ItemKeyedDataSource

    To obtain the next data according to the data on a

  • PositionalDataSource

    Starts loading the specified position

Advanced use of these three data sources, please refer to the official documentation and examples :

3. Create a database

Database interface display provides data to support the current sample program, when the database is created, insert the preset data.

  • It must be added @Database comment

  • Data must be declared list information

  • Abstract methods must contain no arguments, returns the class annotated with @Dao

  • It must be abstract classes and inheritance RoomDatabase

@Database(entities = arrayOf(Cheese::class), version = 1)
abstract class CheeseDb : RoomDatabase() {
    abstract fun cheeseDao(): CheeseDao//返回DAO
    ...
    //获取数据库实例,同步且单例
    @Synchronized
    fun get(context: Context): CheeseDb {
        if (instance == null) {
            instance = Room.databaseBuilder(context.applicationContext,
                    CheeseDb::class.java, "CheeseDatabase")
                    .addCallback(object : RoomDatabase.Callback() {
                        override fun onCreate(db: SupportSQLiteDatabase) {
                            //数据库创建时插入预置数据
                            fillInDb(context.applicationContext)
                        }
                    }).build()
        }
        return instance!!
    }
    
    private fun fillInDb(context: Context) {
        // inserts in Room are executed on the current thread, so we insert in the background
        // CHEESE_DATA为默认数据列表
        ioThread {
            get(context).cheeseDao().insert(
                    CHEESE_DATA.map { Cheese(id = 0, name = it) })
        }
    }
}

复制代码

4. Create a ViewModel

Create a custom ViewModel provide support for the interface and data processing. Which contains the DAO, data list information.

class CheeseViewModel(app: Application) : AndroidViewModel(app) {
    val dao = CheeseDb.get(app).cheeseDao()
    val allCheeses = dao.allCheesesByName().toLiveData(Config(
        pageSize = 30,//指定页面显示的数据项数量
        enablePlaceholders = true,//是否允许使用占位符
        maxSize = 200 //一次性加载数据的最大数量
      ),
      fetchExecutor = Executor {  }//自定义Executor更好地控制paging库何时从应用程序的数据库中加载列表
    )
    
    fun insert(text: CharSequence) = ioThread {
        dao.insert(Cheese(id = 0, name = text.toString()))
    }

    fun remove(cheese: Cheese) = ioThread {
        dao.delete(cheese)
    }
}
复制代码

Tip : Custom ViewModel directly inherited AndroidViewModel, where you can do some of the resources depends on the Context of acquisition and other functions.

public class AndroidViewModel extends ViewModel {
    ...
    public <T extends Application> T getApplication() {
        return (T) mApplication;
    }
}
复制代码

Create a ViewModel, including access and update data:

  • Gets a list of the database by DAO
  • Use LiveData component management data
  • Increased paging support (pageSize, enablePlaceholders, maxSize) function
  • Add custom Executor

Paging page is dependent on the length of the component, a placeholder property to the maximum length of the three pieces of display data loading.

Page size number of entities per page:

Maximum length : prefetching also known length, this value should be several times the size pageSize (specific projects commissioning according to actual situation)

Placeholders : If set to true, was not yet finished loading the list of items displayed placeholders

Requires the use of placeholders number of data can be set, the default display, the data items have the same size display view , has the following advantages:

  • Provide full support scroll bars
  • More items without displaying the load

Binding interface

Data is ready, you can start to bind and display interface.

The interface displays, the need to provide adapter with RecyclerView bound to note the use of Paging paging load, adapter needs to inherit from PagedListAdapter.


class CheeseAdapter : PagedListAdapter<Cheese, CheeseViewHolder>(diffCallback) {
    override fun onBindViewHolder(holder: CheeseViewHolder, position: Int) {
        holder.bindTo(getItem(position))
    }

    override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): CheeseViewHolder =
            CheeseViewHolder(parent)
    companion object {
        //根据diffCallback来确认新加载的数据是否与旧数据有差异,确定是否更新显示
        private val diffCallback = object : DiffUtil.ItemCallback<Cheese>() {
            override fun areItemsTheSame(oldItem: Cheese, newItem: Cheese): Boolean =
                    oldItem.id == newItem.id
            //kotlin使用==会将对象的内容进行对比,使用java需要重写equals方法并替换
            override fun areContentsTheSame(oldItem: Cheese, newItem: Cheese): Boolean =
                    oldItem == newItem
        }
    }
}

//ViewHolder的实现比较简单,将Cheese数据更新到TextView
class CheeseViewHolder(parent :ViewGroup) : RecyclerView.ViewHolder(
        LayoutInflater.from(parent.context).inflate(R.layout.cheese_item, parent, false)) {

    private val nameView = itemView.findViewById<TextView>(R.id.name)
    var cheese : Cheese? = null

    //未绑定数据,或者打开占位符后快速滑动会出现cheese为null,实际项目中需要
    //处理此种情况,数据加载时会重新rebind
    fun bindTo(cheese : Cheese?) {
        this.cheese = cheese
        nameView.text = cheese?.name
    }
}
复制代码

class MainActivity : AppCompatActivity() {
private val viewModel by viewModels<CheeseViewModel>()//创建viewModel
override fun onCreate(savedInstanceState: Bundle?) {
    ...
    val adapter = CheeseAdapter()//继承PagedListAdapter的类对象
    cheeseList.adapter = adapter //为RecyclerView添加适配器
    //viewmodel数据与adapter绑定,在数据变化时通知adapter更新UI
    viewModel.allCheeses.observe(this, Observer(adapter::submitList))
    initSwipeToDelete()//设置左滑/右滑删除数据项
    initAddButtonListener()//设置点击添加Cheese功能
    ...
}
复制代码

Well, you're done!

final effect



You can also try using only the network or network database + mode feature development.

Source here:


Database Paging

Demand paging network


Welcome attention to public numbers, messages discuss more technical issues

file

Guess you like

Origin juejin.im/post/5d9e98825188257d464f05f2