当前位置:   article > 正文

Android Kotlin Paging3 Flow完整教程_kotlin之paging3

kotlin之paging3

准备好接口

  1. package com.example.android_learn_paging.net
  2. import com.example.android_learn_paging.model.NetDataList
  3. import retrofit2.http.GET
  4. import retrofit2.http.Query
  5. interface FeedBackApi {
  6. @GET("api/v1/open/test")
  7. suspend fun getFeedBack(
  8. @Query("page") page: Int,
  9. @Query("size") size: Int
  10. ): NetDataList
  11. }

用laravel8.5 写的 就是对数据库进行分页查询

  1. public function index($size, $search): JsonResponse
  2. {
  3. $res = AppFeedBack::query();
  4. if ($search) {
  5. if (isset($search['app_name'])) {
  6. $res = $res->where('app_name', 'like', "%" . $search['app_name'] . "%");
  7. }
  8. if (isset($search['content'])) {
  9. $res = $res->where('content', 'like', "%" . $search['content'] . "%");
  10. }
  11. }
  12. $data = $res
  13. ->orderBy('create_time', 'desc')
  14. ->paginate($size)
  15. ->toArray();
  16. // return AppFeedBack::simplePaginate(15);
  17. return $this->apiSuccess("", $data);
  18. }

大概给大家看下工程结构

很复杂。

 返回的数据bean格式如下

  1. package com.example.android_learn_paging.model
  2. data class FeedBack(
  3. val id: Int, val app_name: String, val package_name: String, val content: String
  4. )
  5. data class FeedBacks(
  6. val current_page: Int, val data: ArrayList<FeedBack>, val last_page: Int
  7. )
  8. data class NetDataList(
  9. val code: Int, val message: String, val data: FeedBacks
  10. )

api接口

  1. package com.example.android_learn_paging.net
  2. import com.example.android_learn_paging.model.NetDataList
  3. import retrofit2.http.GET
  4. import retrofit2.http.Query
  5. interface FeedBackApi {
  6. @GET("api/v1/open/test")
  7. suspend fun getFeedBack(
  8. @Query("page") page: Int,
  9. @Query("size") size: Int
  10. ): NetDataList
  11. }

初始化数据类

  1. package com.example.android_flow_practice.net
  2. import android.util.Log
  3. import okhttp3.OkHttpClient
  4. import okhttp3.logging.HttpLoggingInterceptor
  5. import retrofit2.Retrofit
  6. import retrofit2.converter.gson.GsonConverterFactory
  7. import retrofit2.create
  8. object RetrofitClient {
  9. val url = "https://xxx.xxxx.xxxx";
  10. private const val TAG = "RetrofitClient"
  11. private val instance: Retrofit by lazy {
  12. val interceptor = HttpLoggingInterceptor(HttpLoggingInterceptor.Logger {
  13. Log.w(TAG, "${it}")
  14. })
  15. interceptor.level = HttpLoggingInterceptor.Level.BODY;
  16. Retrofit.Builder().client(
  17. OkHttpClient.Builder().addInterceptor(interceptor).build()
  18. ).baseUrl(url)
  19. .addConverterFactory(GsonConverterFactory.create())
  20. .build()
  21. }
  22. fun <T> createApi(clazz: Class<T>): T {
  23. return instance.create(clazz) as T
  24. }
  25. }

用到了三方依赖

  1. implementation "androidx.activity:activity-ktx:1.5.1"
  2. implementation "androidx.fragment:fragment-ktx:1.5.2"
  3. implementation "androidx.lifecycle:lifecycle-livedata-ktx:2.5.1"
  4. implementation "androidx.swiperefreshlayout:swiperefreshlayout:1.1.0"
  5. implementation "com.squareup.retrofit2:retrofit:2.9.0"
  6. implementation "com.squareup.retrofit2:converter-gson:2.9.0"
  7. implementation "com.squareup.okhttp3:logging-interceptor:3.4.1"
  8. implementation "androidx.paging:paging-runtime-ktx:3.1.1"
  9. implementation "com.squareup.picasso:picasso:2.71828"

 

 这俩勾上 kapt加上

好了 

继续

AppConfigs
  1. package com.example.android_learn_paging.config
  2. object AppConfigs {
  3. const val LOAD_PAGE = 8
  4. //默认就是3
  5. const val INITPAGE_SIZE = LOAD_PAGE * 3
  6. }

paging3 的一次加载页数 。这里写死了。为啥要这么写 因为后面就知道了

核心adapter PageSource

FeedBackPagingSource
  1. package com.example.android_learn_paging.paging
  2. import android.util.Log
  3. import androidx.paging.PagingSource
  4. import androidx.paging.PagingState
  5. import com.example.android_flow_practice.net.RetrofitClient
  6. import com.example.android_learn_paging.config.AppConfigs
  7. import com.example.android_learn_paging.model.FeedBack
  8. import com.example.android_learn_paging.net.FeedBackApi
  9. import kotlinx.coroutines.delay
  10. class FeedBackPagingSource : PagingSource<Int, FeedBack>() {
  11. private val TAG = "FeedBackPagingSource"
  12. /**
  13. * 1 8
  14. * 2 8
  15. * 3 8
  16. *
  17. *
  18. * null 2
  19. * 1 3
  20. * 2 4
  21. *
  22. *
  23. * 1 24
  24. * 2 8
  25. * 3 8
  26. *
  27. *
  28. * null 4
  29. * 3 5
  30. * 4 6
  31. */
  32. override suspend fun load(params: LoadParams<Int>): LoadResult<Int, FeedBack> {
  33. val currentPage = params.key ?: 1
  34. val pageSize = params.loadSize
  35. val feedbacks =
  36. RetrofitClient.createApi(FeedBackApi::class.java).getFeedBack(currentPage, pageSize)
  37. Log.d(TAG, "load: currentPage $currentPage ,pageSize $pageSize")
  38. var prevKey: Int? = null
  39. var nextKey: Int? = null
  40. if (currentPage == 1) {
  41. prevKey = null
  42. nextKey = AppConfigs.INITPAGE_SIZE / AppConfigs.LOAD_PAGE + 1
  43. } else {
  44. prevKey = currentPage - 1
  45. nextKey =
  46. if (feedbacks.data.last_page > feedbacks.data.current_page) currentPage + 1 else null
  47. }
  48. return try {
  49. LoadResult.Page(data = feedbacks.data.data, prevKey = prevKey, nextKey = nextKey)
  50. } catch (e: Exception) {
  51. e.printStackTrace()
  52. return LoadResult.Error(e)
  53. }
  54. }
  55. override fun getRefreshKey(state: PagingState<Int, FeedBack>): Int? {
  56. return null
  57. }
  58. }

 由于第一次加载了三页数 所以再次加载不可以直接从2开始

 这个注释上面是指的

第一次 加载8个  对应下面  首次加载为null 然后下一页为2

第二次  8个  对应下面  第二次 加载  上一页为 1 然后下一页为3 、

这都是理想情况下 当如果是分页首次加载了 initialLoadSize  为默认3倍的时候要处理就往下面数

就懂了

viewModel记录了我们在如何处理这个数据 并且返回了FLow

  1. package com.example.android_learn_paging.viewmodel
  2. import androidx.lifecycle.ViewModel
  3. import androidx.lifecycle.viewModelScope
  4. import androidx.paging.Pager
  5. import androidx.paging.PagingConfig
  6. import androidx.paging.PagingData
  7. import androidx.paging.cachedIn
  8. import com.example.android_learn_paging.config.AppConfigs
  9. import com.example.android_learn_paging.model.FeedBack
  10. import com.example.android_learn_paging.paging.FeedBackPagingSource
  11. import kotlinx.coroutines.flow.Flow
  12. class FeedBackViewModel : ViewModel() {
  13. private val moives by lazy {
  14. Pager(config = PagingConfig(
  15. pageSize = AppConfigs.LOAD_PAGE,
  16. initialLoadSize = AppConfigs.INITPAGE_SIZE,
  17. prefetchDistance = 1
  18. ), pagingSourceFactory = { FeedBackPagingSource() }).flow.cachedIn(viewModelScope)
  19. }
  20. fun loadFeedBack(): Flow<PagingData<FeedBack>> = moives
  21. }

下面就是页面代码了。

先 MainActivity吧

  1. package com.example.android_learn_paging.activity
  2. import androidx.appcompat.app.AppCompatActivity
  3. import android.os.Bundle
  4. import androidx.activity.viewModels
  5. import androidx.lifecycle.lifecycleScope
  6. import androidx.paging.LoadState
  7. import com.example.android_learn_paging.adapter.FeedBackAdapter
  8. import com.example.android_learn_paging.adapter.MovieLoadMoreAdapter
  9. import com.example.android_learn_paging.databinding.ActivityMainBinding
  10. import com.example.android_learn_paging.viewmodel.FeedBackViewModel
  11. import kotlinx.coroutines.flow.collectLatest
  12. class MainActivity : AppCompatActivity() {
  13. private val feedBackViewModel: FeedBackViewModel by viewModels()
  14. private val mBinding: ActivityMainBinding by lazy {
  15. ActivityMainBinding.inflate(layoutInflater)
  16. }
  17. override fun onCreate(savedInstanceState: Bundle?) {
  18. super.onCreate(savedInstanceState)
  19. setContentView(mBinding.root)
  20. val feedBackAdapter = FeedBackAdapter(this)
  21. mBinding.apply {
  22. rv.adapter =
  23. feedBackAdapter.withLoadStateFooter(MovieLoadMoreAdapter(this@MainActivity))
  24. swipeRefreshLayout.setOnRefreshListener {
  25. feedBackAdapter.refresh()
  26. }
  27. }
  28. lifecycleScope.launchWhenCreated {
  29. feedBackViewModel.loadFeedBack().collectLatest {
  30. feedBackAdapter.submitData(it)
  31. }
  32. }
  33. lifecycleScope.launchWhenCreated {
  34. feedBackAdapter.loadStateFlow.collectLatest { status ->
  35. mBinding.swipeRefreshLayout.isRefreshing = status.refresh is LoadState.Loading
  36. }
  37. }
  38. }
  39. }

布局

  1. <?xml version="1.0" encoding="utf-8"?>
  2. <androidx.constraintlayout.widget.ConstraintLayout xmlns:android="http://schemas.android.com/apk/res/android"
  3. xmlns:app="http://schemas.android.com/apk/res-auto"
  4. xmlns:tools="http://schemas.android.com/tools"
  5. android:layout_width="match_parent"
  6. android:layout_height="match_parent"
  7. tools:context=".activity.MainActivity">
  8. <androidx.swiperefreshlayout.widget.SwipeRefreshLayout
  9. android:id="@+id/swipeRefreshLayout"
  10. android:layout_width="match_parent"
  11. android:layout_height="match_parent">
  12. <androidx.recyclerview.widget.RecyclerView
  13. android:id="@+id/rv"
  14. android:layout_width="match_parent"
  15. android:layout_height="wrap_content"
  16. android:orientation="vertical"
  17. app:layoutManager="androidx.recyclerview.widget.LinearLayoutManager" />
  18. </androidx.swiperefreshlayout.widget.SwipeRefreshLayout>
  19. </androidx.constraintlayout.widget.ConstraintLayout>

看到了很多生代码 没关系 一点一点补充

FeedBackAdapter.kt
  1. package com.example.android_learn_paging.adapter
  2. import android.content.Context
  3. import android.view.LayoutInflater
  4. import android.view.ViewGroup
  5. import androidx.paging.PagingDataAdapter
  6. import androidx.recyclerview.widget.DiffUtil
  7. import com.example.android_learn_paging.databinding.ItemPagingBinding
  8. import com.example.android_learn_paging.model.FeedBack
  9. val diffUtil = object : DiffUtil.ItemCallback<FeedBack>() {
  10. //如果id一样 就认为是同一个元素
  11. override fun areContentsTheSame(oldItem: FeedBack, newItem: FeedBack): Boolean {
  12. return oldItem.id == newItem.id
  13. }
  14. override fun areItemsTheSame(oldItem: FeedBack, newItem: FeedBack): Boolean {
  15. return oldItem == newItem
  16. }
  17. }
  18. class FeedBackAdapter(private val context: Context) :
  19. PagingDataAdapter<FeedBack, BindingViewHolder>(diffUtil) {
  20. override fun onBindViewHolder(holder: BindingViewHolder, position: Int) {
  21. val feedBack = getItem(position)
  22. feedBack?.let {
  23. val binding = holder.binding as ItemPagingBinding
  24. binding.feedback = it
  25. binding.packageName = "https://www.baidu.com/s?wd=%E7%99%BE%E5%BA%A6%E7%83%AD%E6%90%9C&sa=ire_dl_gh_logo_texing&rsv_dl=igh_logo_pc";
  26. }
  27. }
  28. override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): BindingViewHolder {
  29. // val binding = Pagint
  30. val binding = ItemPagingBinding.inflate(LayoutInflater.from(context), parent, false)
  31. return BindingViewHolder(binding)
  32. }
  33. }
BindingViewHolder
  1. package com.example.android_learn_paging.adapter
  2. import androidx.recyclerview.widget.RecyclerView
  3. import androidx.viewbinding.ViewBinding
  4. class BindingViewHolder(val binding: ViewBinding) : RecyclerView.ViewHolder(binding.root) {}
ImageViewBindingAdapter

 对app:image标签来进行注解

  1. package com.example.android_learn_paging.adapter
  2. import android.graphics.Color
  3. import com.example.android_learn_paging.R
  4. import android.text.TextUtils
  5. import android.widget.ImageView
  6. import androidx.databinding.BindingAdapter
  7. import com.squareup.picasso.Picasso
  8. class ImageViewBindingAdapter {
  9. companion object {
  10. @JvmStatic
  11. @BindingAdapter("image")
  12. fun setImage(imageView: ImageView, url: String) {
  13. if (!TextUtils.isEmpty(url)) {
  14. Picasso.get().load(url).placeholder(R.drawable.ic_launcher_background)
  15. .into(imageView)
  16. } else {
  17. imageView.setBackgroundColor(Color.GRAY)
  18. }
  19. }
  20. }
  21. }

 下拉加载更多组件

MovieLoadMoreAdapter
  1. package com.example.android_learn_paging.adapter
  2. import android.content.Context
  3. import android.view.LayoutInflater
  4. import android.view.ViewGroup
  5. import androidx.paging.LoadState
  6. import androidx.paging.LoadStateAdapter
  7. import com.example.android_learn_paging.databinding.FeedbackLoadmoreBinding
  8. class MovieLoadMoreAdapter(private val context: Context) : LoadStateAdapter<BindingViewHolder>() {
  9. override fun onBindViewHolder(holder: BindingViewHolder, loadState: LoadState) {
  10. }
  11. override fun onCreateViewHolder(parent: ViewGroup, loadState: LoadState): BindingViewHolder {
  12. val binding = FeedbackLoadmoreBinding.inflate(LayoutInflater.from(context), parent, false)
  13. return BindingViewHolder(binding)
  14. }
  15. }

adapter非常简单

feedback_loadmore.xml
  1. <?xml version="1.0" encoding="utf-8"?>
  2. <androidx.constraintlayout.widget.ConstraintLayout xmlns:android="http://schemas.android.com/apk/res/android"
  3. xmlns:app="http://schemas.android.com/apk/res-auto"
  4. android:layout_width="match_parent"
  5. android:layout_height="50dp"
  6. android:padding="10dp">
  7. <ProgressBar
  8. android:id="@+id/progressBar"
  9. android:layout_width="20dp"
  10. android:layout_height="20dp"
  11. android:layout_marginStart="20dp"
  12. app:layout_constraintBottom_toBottomOf="parent"
  13. app:layout_constraintStart_toStartOf="parent" />
  14. <TextView
  15. android:id="@+id/tv_loading"
  16. android:layout_width="wrap_content"
  17. android:layout_height="wrap_content"
  18. android:text="加载更多数据"
  19. app:layout_constraintBottom_toBottomOf="parent"
  20. app:layout_constraintEnd_toEndOf="parent"
  21. app:layout_constraintStart_toStartOf="@id/progressBar" />
  22. </androidx.constraintlayout.widget.ConstraintLayout>

 adapter item布局

item_paging.xml
  1. <?xml version="1.0" encoding="utf-8"?>
  2. <layout xmlns:android="http://schemas.android.com/apk/res/android"
  3. xmlns:app="http://schemas.android.com/apk/res-auto"
  4. xmlns:tools="http://schemas.android.com/tools">
  5. <data>
  6. <variable
  7. name="packageName"
  8. type="String" />
  9. <variable
  10. name="feedback"
  11. type="com.example.android_learn_paging.model.FeedBack" />
  12. </data>
  13. <androidx.constraintlayout.widget.ConstraintLayout
  14. android:layout_width="match_parent"
  15. android:layout_height="wrap_content">
  16. <TextView
  17. android:id="@+id/tv_name"
  18. android:layout_width="wrap_content"
  19. android:layout_height="wrap_content"
  20. android:layout_marginTop="20dp"
  21. android:text="@{feedback.app_name}"
  22. app:layout_constraintEnd_toEndOf="parent"
  23. app:layout_constraintStart_toStartOf="parent"
  24. app:layout_constraintTop_toTopOf="parent" />
  25. <TextView
  26. android:id="@+id/tv_context"
  27. android:layout_width="wrap_content"
  28. android:layout_height="wrap_content"
  29. android:layout_marginTop="10dp"
  30. android:text="@{feedback.content}"
  31. app:layout_constraintEnd_toEndOf="parent"
  32. app:layout_constraintStart_toStartOf="parent"
  33. app:layout_constraintTop_toBottomOf="@+id/tv_name" />
  34. <ImageView
  35. android:layout_width="wrap_content"
  36. android:layout_height="wrap_content"
  37. android:layout_marginTop="10dp"
  38. app:image="@{packageName}"
  39. app:layout_constraintEnd_toEndOf="parent"
  40. app:layout_constraintStart_toStartOf="parent"
  41. app:layout_constraintTop_toBottomOf="@+id/tv_context" />
  42. </androidx.constraintlayout.widget.ConstraintLayout>
  43. </layout>

 系统的adapter指向一个上滑加载更多

下拉刷新用的是

 

 检测刷新状态 并且刷新代码

是的 

PagingDataAdapter 有自己的刷新代码。。而我是刚知道。。我以前都去刷新

 

他 先通过一个成员变量引用出来 然后调用

.invalidate()

进行刷新 也是可行的。。。但是不知道会不会有什么潜在问题。

声明:本文内容由网友自发贡献,不代表【wpsshop博客】立场,版权归原作者所有,本站不承担相应法律责任。如您发现有侵权的内容,请联系我们。转载请注明出处:https://www.wpsshop.cn/w/不正经/article/detail/286615
推荐阅读
相关标签
  

闽ICP备14008679号