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:
Read database page load
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:
- Notes contain @Database
- It is an abstract class that inherits from RoomDatabase
- Notes contain information in the list of entities
- 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:
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 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:
- Create a data entity classes Cheese
- Create a database approach DAO
- Create a database CheeseDb
- 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!
You can also try using only the network or network database + mode feature development.
Source here: