当前位置: 首页 > news >正文

Android 之 Jetpack - Paging

以下是一个基于 Android Paging 3 库的完整 Kotlin 实现示例,结合网络分页场景,包含核心组件和最佳实践:

1. ​​添加依赖(build.gradle)​

dependencies {implementation "androidx.paging:paging-runtime-ktx:3.2.1"implementation "org.jetbrains.kotlinx:kotlinx-coroutines-android:1.7.3"implementation "com.squareup.retrofit2:retrofit:2.9.0" // 网络请求
}

2. ​​定义数据模型

data class Article(val id: Long,val title: String,val content: String
)

3. ​​实现 PagingSource(网络数据源)​

class ArticlePagingSource(private val apiService: ApiService
) : PagingSource<Int, Article>() {override suspend fun load(params: LoadParams<Int>): LoadResult<Int, Article> {return try {val page = params.key ?: 1 // 起始页码val response = apiService.getArticles(page, params.loadSize)LoadResult.Page(data = response.articles,prevKey = if (page > 1) page - 1 else null, // 上一页nextKey = if (response.hasMore) page + 1 else null // 下一页)} catch (e: Exception) {LoadResult.Error(e) // 自动传递错误到UI }}override fun getRefreshKey(state: PagingState<Int, Article>): Int? {// 根据滚动位置计算刷新后的起始页码 return state.anchorPosition?.let { anchorPosition ->state.closestPageToPosition(anchorPosition)?.prevKey?.plus(1)?: state.closestPageToPosition(anchorPosition)?.nextKey?.minus(1)}}
}

4. ​​在 ViewModel 中配置数据流

class ArticleViewModel : ViewModel() {private val api = RetrofitClient.create<ApiService>()val articles: Flow<PagingData<Article>> = Pager(config = PagingConfig(pageSize = 20,          // 每页数据量prefetchDistance = 5,   // 提前加载下一页的阈值(item数量)enablePlaceholders = false // 禁用占位符提升性能 ),pagingSourceFactory = { ArticlePagingSource(api) }).flow.cachedIn(viewModelScope) // 防止重复创建数据流 
}

5. ​​实现 PagingDataAdapter​

class ArticleAdapter : PagingDataAdapter<Article, ArticleAdapter.ViewHolder>(DIFF_CALLBACK) {companion object {private val DIFF_CALLBACK = object : DiffUtil.ItemCallback<Article>() {override fun areItemsTheSame(oldItem: Article, newItem: Article): Boolean =oldItem.id == newItem.id // ID 相同视为同一项[5](@ref)override fun areContentsTheSame(oldItem: Article, newItem: Article): Boolean =oldItem == newItem // 内容完全一致}}inner class ViewHolder(itemView: View) : RecyclerView.ViewHolder(itemView) {fun bind(article: Article) {itemView.findViewById<TextView>(R.id.title).text = article.title}}override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): ViewHolder {val view = LayoutInflater.from(parent.context).inflate(R.layout.item_article, parent, false)return ViewHolder(view)}override fun onBindViewHolder(holder: ViewHolder, position: Int) {getItem(position)?.let { holder.bind(it) }}
}

6. ​​在 Activity/Fragment 中集成

class ArticleListFragment : Fragment() {private lateinit var binding: FragmentArticleListBindingprivate val viewModel: ArticleViewModel by viewModels()private val adapter = ArticleAdapter()override fun onViewCreated(view: View, savedInstanceState: Bundle?) {binding.recyclerView.apply {layoutManager = LinearLayoutManager(requireContext())adapter = this@ArticleListFragment.adapter}// 监听加载状态(加载中/错误/完成)adapter.addLoadStateListener { loadState ->when (loadState.refresh) {is LoadState.Loading -> showProgressBar()is LoadState.Error -> showError("加载失败")is LoadState.NotLoading -> hideProgressBar()}}// 绑定数据流lifecycleScope.launch {viewModel.articles.collectLatest { pagingData ->adapter.submitData(pagingData) // 提交分页数据 }}}// 错误重试逻辑private fun showError(message: String) {binding.errorText.text = messagebinding.retryButton.setOnClickListener { adapter.retry() } // 自动重试失败页 }
}

关键配置说明​

  1. ​PagingConfig 参数优化​

    • prefetchDistance:建议设为屏幕可见项数量的 1-2 倍(如 5-10)

    • maxSize:设置缓存上限(默认 200),防止内存溢出

    • enablePlaceholders:关闭占位符可减少内存占用

  2. ​错误处理机制​

    • adapter.retry():自动重试失败的分页请求

    • LoadResult.Error:在 UI 层通过 LoadState监听错误

  3. ​性能优化​

    • cachedIn(viewModelScope)​:避免重复创建数据流

    • ​高效 DiffUtil​​:通过 areItemsTheSame快速比较数据变更

http://www.dtcms.com/a/320062.html

相关文章:

  • 《C语言》函数练习题--2
  • ElasticSearch相关术语介绍
  • 使用 decimal 包解决 go float 浮点数运算失真
  • 小鸡模拟器安卓版:经典街机游戏的移动体验
  • 利用Axure与JavaScript打造动态图片上传原型:设计案例分享
  • spring-cglib代理-初探01
  • 深度学习-卷积神经网络CNN-1×1卷积层
  • Flink-1.19.0源码详解9-ExecutionGraph生成-后篇
  • UE5多人MOBA+GAS 39、制作角色上半身UI
  • 字符串匹配(重点解析KMP算法)
  • 6 大模块!重构物业运营方式
  • 跨境电商增长突围:多维变局下的战略重构与技术赋能
  • 数智先锋 | Bonree ONE 赋能通威股份有限公司提升全栈可观测性能力
  • 深入解析NVIDIA Nsight工具套件:原理、功能与实战指南
  • 房产证识别在房产行业的技术实现及应用原理
  • Python Socket 脚本深度解析与开发指南
  • 扣扣号码展示网站源码_号码售卖展示系统源码 全开源 带后台(源码下载)
  • 5、倒计时翻页效果
  • 工作任务管理
  • 《C语言》指针练习题--1
  • Python入门Day17:函数式编程(map/filter/reduce/lambda)
  • 浏览器渲染与GPU进程通信图解
  • Numpy科学计算与数据分析:Numpy数组操作入门:合并、分割与重塑
  • PWM常用库函数(STC8系列)
  • 【Linux基础知识系列】第八十七篇 - 使用df命令查看磁盘空间
  • 橙河网络:Cint站点如何注册?好做吗?
  • 街道垃圾识别准确率↑32%:陌讯多模态融合算法实战解析
  • 解锁制药新质生产力:合规与效率双赢的数字化转型之道
  • 基于肌电信号的神经网络动作识别系统
  • docker mysql 5.6