Android Jetpack Kotlin/Java pageing3的基础使用。
Posted 安果移不动
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了Android Jetpack Kotlin/Java pageing3的基础使用。相关的知识,希望对你有一定的参考价值。
Java可以参考
2021年最全面的Jetpack系统学习课程,看他就够了,更新中_哔哩哔哩_bilibili
Kt可以参考
郭林大神的:
Jetpack新成员,Paging3从吐槽到真香_guolin的博客-CSDN博客_paging3
他里面提供了数据接口 我没有提供。
美中不足的地方我又找到了。
他这个地方用的是loadSize
第一个开始都是好好的
但是后面会变为前面的三倍,也就是
分页数据
第一次正常1
第二次正常2
第三次正常3
第四次 不正常 1= 第一次的数据
第五次 不正常2 = 第二次的数据
第六次 不正常2 = 第三次的数据
第七次 不正常 = 正常第四次的数据
第八次 不正常 = 正常第五次的数据
----以此类推。
这可能和我用的版本比较新有关。但这是需要大家注意的地方 当数据拉下来要自己检查检查对不对。合格不合格 再进行微调
我的粗略了看了下他的数据是上面这样分布的有问题的
我的数据是刚好没有问题的
所以我这个pageSize还是老实点写死就好啦。
但是。也可以参考我这篇哦。
为啥说起paging。好像闻所未闻?
因为他需要更多的jetpack知识才可以进行学习。否则还是难以理解
协程 的挂起 suspend 、Flow的流、MVVM 的viewModel
下面是正式的kt教程
implementation 'com.squareup.retrofit2:retrofit:2.9.0'
implementation 'com.squareup.retrofit2:converter-gson:2.9.0'
implementation 'androidx.paging:paging-runtime-ktx:3.1.1'
请注意 java依赖都不带 -ktx
和kt完全不一样,java的朋友真的需要学习kt才好用。当然如果不想学可以看java的那个教程
java应该是这样
androidx.paging:paging-runtime:3.1.1
下面关于java不再多讲。主要是kt
引入依赖后同步工程
这里我就不提供具体的分页请求接口数据了。
所以域名和路径我都打上马赛克。但是绝对是可以正常访问的数据按照这种格式绝对没有问题的哦
然后 编写网络类
package com.anguomob.jecpack.api
import android.util.Log
import okhttp3.OkHttpClient
import retrofit2.Retrofit
import retrofit2.adapter.rxjava2.RxJava2CallAdapterFactory
import retrofit2.converter.gson.GsonConverterFactory
import java.util.concurrent.TimeUnit
class RetrofitClient private constructor()
private val TAG = "RetrofitClient"
companion object
val instance by lazy(mode = LazyThreadSafetyMode.SYNCHRONIZED)
RetrofitClient()
val BASE_URL = "https://xxxxx.xxxxx.com/"
private fun getClient(): OkHttpClient
return OkHttpClient.Builder().build()
fun getApi(): Api
Log.e(TAG, "getApi:$BASE_URL ")
val builder = RetrofitUrlManager.with(OkHttpClient.Builder())
// 拦截器
val client: OkHttpClient = builder
.addNetworkInterceptor(OkHttpNetworkInterceptor())
.readTimeout(60, TimeUnit.SECONDS)
.writeTimeout(60, TimeUnit.SECONDS)
.connectTimeout(60, TimeUnit.SECONDS)
.retryOnConnectionFailure(true)
.build()
return Retrofit.Builder()
// .client(client)
.baseUrl(BASE_URL)
// .addCallAdapterFactory(RxJava2CallAdapterFactory.create())
.addConverterFactory(GsonConverterFactory.create())
.build().create(Api::class.java)
// .client(client)
这个可以先不设置。还有 回调我们也不用RxJava了。而是使用协程的suspend
package com.anguomob.jecpack.api
import com.anguomob.jecpack.bean.FeedBack
import com.anguomob.jecpack.bean.FeedBackLists
import com.anguomob.jecpack.bean.FeedBackParent
import com.anguomob.jecpack.bean.Response
import io.reactivex.Observable
import retrofit2.Call
import retrofit2.http.GET
import retrofit2.http.Query
interface Api
@GET("api/v1/open/app/xxxx/xxxx")
suspend fun getFeedBack(
@Query("page") page: Int,
@Query("size") size: Int
): Response<FeedBackLists>
这里大家可以看到。有一个Response<FeedBackLists>
Response 是对最外层数据的封装。比如 后台一般要返回code、data、message
package com.anguomob.jecpack.bean
import com.google.gson.annotations.SerializedName
import java.io.Serializable
/**
*
* 返回结果封装
*/
class Response<T> : Serializable
@SerializedName("code")
var code // 返回的code
= 0
@SerializedName("data")
var data // 具体的数据结果
: T? = null
private set
@SerializedName("message")
var message // message 可用来返回接口的说明
: String? = null
fun setData(data: T)
this.data = data
其中数据bean为
package com.anguomob.jecpack.bean
import com.google.gson.annotations.SerializedName
data class FeedBackLists(
//起始位置
val current_page: Int,
val `data`: List<FeedBack>,
val first_page_url: String,
val from: Int,
val last_page: Int,
val last_page_url: String,
val next_page_url: String,
val path: String,
val per_page: Int,
val prev_page_url: Any,
val to: Int,
//一共多少条
val total: Int,
//当前页面的数量
var count: Int = data.size,
)
data class FeedBack(
val id: Int,
val content: String,
val create_time: String,
// val android_version: String,
// val app_name: String,
// val app_version: String,
// val contact: String,
// val device_unique_id: String,
// val model: String,
// val package_name: String
)
稍微解释一下。
FeedBackLists 里面的
data 是本次分页的数据
而
FeedBackLists 包含了
last_page 这个代表 当前size下,总共分页多少页。
将数据挂起后 可以直接将数据返回出来
我们编写第一个核心类
PagingSource
加载数据
package com.anguomob.jecpack.paging
import android.util.Log
import androidx.paging.PagingSource
import androidx.paging.PagingState
import com.anguomob.jecpack.api.RetrofitClient
import com.anguomob.jecpack.bean.FeedBack
import com.anguomob.jecpack.bean.FeedBackLists
class FeedBackDataSource : PagingSource<Int, FeedBack>()
private val TAG = "FeedBackDataSource"
override fun getRefreshKey(state: PagingState<Int, FeedBack>): Int?
return null
override suspend fun load(params: LoadParams<Int>): PagingSource.LoadResult<Int, FeedBack>
return try
val page = params.key ?: 1 // set page 1 as default
val pageSize = 1
val repoResponse = RetrofitClient
.instance
.getApi()
.getFeedBack(page, pageSize)
val prevKey = if (page > 1) page - 1 else null
val nextKey = if (page < (repoResponse.data?.last_page ?: 0)) page + 1 else null
Log.e(TAG, "load:pageSize:$pageSize")
Log.e(TAG, "load:prevKey:$prevKey")
Log.e(TAG, "load:nextKey:$nextKey")
Log.e(TAG, "load:repoResponse.data?.last_page:$repoResponse.data?.last_page")
PagingSource.LoadResult.Page(repoResponse.data?.data!!, prevKey, nextKey)
catch (e: Exception)
Log.e(TAG, "load:Error:$e.message")
PagingSource.LoadResult.Error(e)
通过
PagingSource.LoadResult.Page
加载数据 并将prePage与nextPage的索引返回出去。还有data列表数据,如若没有数据则返回null
如何使用到DataSourece呢 再次编写一个类
package com.anguomob.jecpack.repository
import androidx.paging.Pager
import androidx.paging.PagingConfig
import androidx.paging.PagingData
import com.anguomob.jecpack.bean.FeedBack
import com.anguomob.jecpack.paging.FeedBackDataSource
import kotlinx.coroutines.flow.Flow
object FeedBackRepository
private const val PAGE_SIZE = 1
fun getPagingData(): Flow<PagingData<FeedBack>>
return Pager(
config = PagingConfig(PAGE_SIZE),
pagingSourceFactory = FeedBackDataSource()
).flow
可以配置页数大小。
如何使用到这个仓库数据
FeedBackRepository
呢
viewModel
package com.anguomob.jecpack.viewmodel
import androidx.lifecycle.ViewModel
import androidx.lifecycle.viewModelScope
import androidx.paging.PagingData
import androidx.paging.cachedIn
import com.anguomob.jecpack.bean.FeedBack
import com.anguomob.jecpack.repository.FeedBackRepository
import kotlinx.coroutines.flow.Flow
class FeedBackViewModel : ViewModel()
fun getPagingData(): Flow<PagingData<FeedBack>>
return FeedBackRepository.getPagingData().cachedIn(viewModelScope)
页面布局很简单。
<?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/rv"
android:layout_width="match_parent"
android:layout_height="match_parent"
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintTop_toTopOf="parent" />
</androidx.constraintlayout.widget.ConstraintLayout>
页面代码也很简单 因为核心代码都在其他的pageSource中
package com.anguomob.jecpack.activity
import androidx.appcompat.app.AppCompatActivity
import android.os.Bundle
import android.util.Log
import androidx.lifecycle.ViewModelProvider
import androidx.lifecycle.lifecycleScope
import androidx.recyclerview.widget.LinearLayoutManager
import com.anguomob.jecpack.R
import com.anguomob.jecpack.adapter.FeedBackListAdapter
import com.anguomob.jecpack.databinding.ActivityPagingBinding
import com.anguomob.jecpack.databinding.ItemPagingBinding
import com.anguomob.jecpack.viewmodel.FeedBackViewModel
import kotlinx.coroutines.flow.collect
import kotlinx.coroutines.launch
class PagingActivity : AppCompatActivity()
private lateinit var binding: ActivityPagingBinding
private val TAG = "PagingActivity"
override fun onCreate(savedInstanceState: Bundle?)
super.onCreate(savedInstanceState)
binding = ActivityPagingBinding.inflate(layoutInflater)
setContentView(binding.root)
binding.rv.layoutManager = LinearLayoutManager(this)
val feedBackListAdapter = FeedBackListAdapter()
binding.rv.adapter = feedBackListAdapter
val viewModel =
ViewModelProvider(this, ViewModelProvider.AndroidViewModelFactory(application)).get(
FeedBackViewModel::class.java
)
lifecycleScope.launch
viewModel.getPagingData().collect( data ->
feedBackListAdapter.submitData(data)
)
但是他却实现了分页加载。
非常的不可思议
但是我们还没有写adapter代码
布局
<?xml version="1.0" encoding="utf-8"?>
<layout 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">
<data>
<variable
name="feedBack"
type="com.anguomob.jecpack.bean.FeedBack" />
</data>
<androidx.constraintlayout.widget.ConstraintLayout
android:layout_width="match_parent"
android:layout_height="wrap_content"
tools:context=".activity.DatabindingActivity">
<TextView
android:id="@+id/imageView"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginStart="28dp"
android:text="@String.valueOf(feedBack.id)"
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toTopOf="parent"
tools:text="1" />
<TextView
android:id="@+id/textView1"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginTop="36dp"
android:text="@feedBack.create_time"
android:textSize="24dp"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintHorizontal_bias="0.471"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toTopOf="parent"
tools:text="姓名" />
<TextView
android:id="@+id/textView2"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginTop="20dp"
android:layout_marginBottom="20dp"
android:text="@feedBack.content"
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintStart_toStartOf="@+id/textView1"
app:layout_constraintTop_toBottomOf="@+id/textView1"
tools:text="年龄" />
</androidx.constraintlayout.widget.ConstraintLayout>
</layout>
这里面我用到了databinding数据绑定
你不想用。也没关系的 用正常的rv绑定数据那样就好 这个我就不教你啦。基础
布局代码
package com.anguomob.jecpack.adapter
import android.util.Log
import android.view.LayoutInflater
import android.view.ViewGroup
import androidx.databinding.DataBindingUtil
import androidx.paging.PagedListAdapter
import androidx.paging.PagingDataAdapter
import androidx.recyclerview.widget.DiffUtil
import androidx.recyclerview.widget.DiffUtil.ItemCallback
import androidx.recyclerview.widget.RecyclerView
import com.anguomob.jecpack.R
import com.anguomob.jecpack.bean.FeedBack
import com.anguomob.jecpack.databinding.ItemPagingBinding
//差分更新 只更新需要的元素 而不是全部更新 性能优化的一种方式
class FeedBackListAdapter :
PagingDataAdapter<FeedBack, FeedBackListAdapter.FeedBackViewHolder>(differCallback)
companion object
private const val TAG = "FeedBackListAdapter"
val differCallback: ItemCallback<FeedBack> = object :
ItemCallback<FeedBack>()
override fun areItemsTheSame(oldItem: FeedBack, newItem: FeedBack): Boolean
return oldItem == newItem
override fun areContentsTheSame(oldItem: FeedBack, newItem: FeedBack): Boolean
return oldItem == newItem
class FeedBackViewHolder(val binding: ItemPagingBinding) :
RecyclerView.ViewHolder(binding.root)
override fun onBindViewHolder(holder: FeedBackListAdapter.FeedBackViewHolder, position: Int)
val item = getItem(position);
Log.e(TAG, "onBindViewHolder: $item", )
if (item != null)
holder.binding.feedBack = item
override fun onCreateViewHolder(
parent: ViewGroup,
viewType: Int
): FeedBackListAdapter.FeedBackViewHolder
val binding: ItemPagingBinding = DataBindingUtil.inflate(
LayoutInflater.from(parent.context),
R.layout.item_paging,
parent,
false
)
return FeedBackViewHolder(binding)
这里你会返现没有getItemCount 相关的方法。因为他们内部帮我们实现了
而且多了一个
differCallback
这个是做什么的
他是为了防止数据重复加载刷新的。
正常情况下我们的分页加载是 加载完毕 data a 然后加载data b 然后调用
notifiyDataChange()
这会有一个小小的瑕疵。他刷新的是全部数据。
但是、这个是
ItemCallback
局部刷新 帮我们仅仅在局部真正变更的部分进行了刷新。我们开发者要是用以往的rv自己处理刷新方式其实是很难做到这点的。要对很多数据做比较。想想都头大。
而现在 实现这个类就好啦 java 建议要对bean重写 hashCode 和 equal方法。kt使用==就好啦。也不用重写方法。
onBindViewHolder 对数据进行设置。
onCreateViewHolder 中对数据视图进行绑定。
好处不仅仅是性能上面。他会加载的很流畅、而且没有那么卡顿的感觉 好像数据都是正常很丝滑一次性没有做分页加载的这样。还有更多的专业配置。
我后续学习了接着往下更新。
刚开始学的时候是想参照java写kt。。后来直接闪退不报错
。。。
再后来看到郭霖大神的博客。才茅塞顿开
依赖都不一样。能用才怪呢、、、
以上是关于Android Jetpack Kotlin/Java pageing3的基础使用。的主要内容,如果未能解决你的问题,请参考以下文章
Android Jetpack架构组件带你了解Android Jetpack
Android Jetpack架构组件——什么是Jetpack?
Android Jetpack架构组件——什么是Jetpack?