Android笔记(二十二):Paging3分页加载库结合Compose的实现网络单一数据源访问

news2024/12/24 21:22:35

Paging3 组件是谷歌公司推出的分页加载库。个人认为Paging3库是非常强大,但是学习难点比较大的一个库。Paging3组件可用于加载和显示来自本地存储或网络中更大的数据集中的数据页面。此方法可让移动应用更高效地利用网络带宽和系统资源。在具体实现上,Paging3与前面的版本完全不同。

一、依赖库的配置

    val paging_version = "3.2.0"
    implementation("androidx.paging:paging-runtime:$paging_version")
    implementation("androidx.paging:paging-compose:$paging_version")

    // optional - RxJava3 support
    implementation("androidx.paging:paging-rxjava3:$paging_version")

    //支持viewmodel
    implementation ("androidx.lifecycle:lifecycle-viewmodel-compose:2.5.1")

    //增加RxJava库的依赖
    implementation("io.reactivex.rxjava3:rxjava:3.0.7")
    implementation("io.reactivex.rxjava3:rxandroid:3.0.0")

    //增加Retrofit库的支持
    implementation("com.squareup.retrofit2:retrofit:2.9.0")
    implementation("com.squareup.retrofit2:converter-gson:2.9.0")

    //增加Retrofit支持RxJava3的CallAdapter
    implementation("com.squareup.retrofit2:adapter-rxjava3:2.9.0")

二、Paging3的架构

Paging3的架构分为三层,如下图所示:
在这里插入图片描述

1.代码库层Repository

PagingSource:定义数据源(来自网络或来自本地数据库,已经从该数据源检索数据);
RemoteMediator:用来处理来自分层数据(例如:来自缓存的网络数据源)的分页

2.ViewModel视图模型层

Pager组件提供了一个公共 API,基于PagingSource 对象和 PagingConfig配置对象来构造在响应式流中公开的 PagingData 实例。
ViewModel 层连接到界面的组件是PagingDataPagingData 对象是用于存放分页数据快照的容器。它会查询PagingSource对象并存储结果。

3.界面层

界面层通过结合Compose组件的LazyColumn以列表方式显示分页加载的数据。

三、网络资源的介绍

为了更好地解释Paging3组件,笔者爬取了一些视频数据保存到本地MySQL数据库,并结合Python+Flaskj将MySQL数据库的保存的数据生成按照页面和每一页展示数据的个数生成对应json数据,通过这种方式获得网络资源。
在这里插入图片描述
这时可以通过浏览器浏览相关内容,类似下图所示:这里传递了两个参数 page表示第几页,size表示页面显示记录数。
在这里插入图片描述
上列展示的json数组包含了多个json对象,每个json对象的格式类似下列形式:

{"actors":"演员",
"directors":"导演",
"intro":"电影简介",
"poster":"http://localhost:5000/photo/s_ratio_poster/public/p2626067725.jpg",
"region":"地区",
"release":"发布年份",
"trailer_url":"https://localhost:5000/trailer/268661/#content",
"video_url":"https://localhost:5000/d04d3c0d2132a29410dceaeefa97e725/view/movie/M/402680661.mp4"}

当然,在具体实现时也可以考虑JavaEE + Tomcat来搭建后台应用。

四、实现Paging3的方式一:PagingSource单一数据源(网络)的处理

在这里,是通过访问单一网络数据源"http://127.0.0.1:5000/film.json?page=1&size=5"来获取分页数据,如上图所示。具体的架构如下图所示:
在这里插入图片描述

1.定义实体类Film

data class Film(
@SerializedName(“name”)
val name:String,
@SerializedName(“release”)
val release:String,
@SerializedName(“region”)
val region:String,
@SerializedName(“directors”)
val directors:String,
@SerializedName(“actors”)
val actors:String,
@SerializedName(“intro”)
val intro:String,
@SerializedName(“poster”)
val poster:String,
@SerializedName(“trailer_url”)
val trailer:String,
@SerializedName(“video_url”)
val video:String
)
在此处说明一下,@SerializedName表示对应json形式的单一film的数据。这样就可以在后续的处理中将请求的json数据根据json数据的关键字获取对应的值,利用这些值生成对应Film对象。

2.网络访问处理

(1)定义网络服务访问接口

interface FilmApiService {
    @GET("film.json")
    suspend fun getData(
        @Query("page") page:Int,
        @Query("size") size:Int
        ):List<Film>
}

此处,page属性对应URL中的page表示页,size表示每一页的记录数;

(2)利用Retrofit构建网络服务

object RetrofitBuilder {
    private const val  BASE_URL = "http://10.0.2.2:5000/"

    private fun getRetrofit(): Retrofit {
        return Retrofit.Builder()
            .baseUrl(BASE_URL)
            .addConverterFactory(GsonConverterFactory.create())
            .build()
    }

    val apiService:FilmApiService =
        getRetrofit().create(FilmApiService::class.java)
}

3.定义代码层

(1)定义代码层要实现的操作接口

interface FilmRepository {
    /**
     * 获取指定page页面的信息
     * @param page Int
     * @return List<Film>
     */
    suspend fun getFilms(page:Int,limit:Int = 5):List<Film>
}

4.P
(2)具体代码层的实现

class FilmRepositoryImp:FilmRepository {

    private val apiService: FilmApiService = RetrofitBuilder.apiService

    override suspend fun getFilms(page: Int,limit:Int): List<Film>
    = apiService.getData(page, limit)

}

4.定义数据源

class FilmSource(private val filmRepository: FilmRepository): PagingSource<Int, Film>() {

    override suspend fun load(params: LoadParams<Int>):LoadResult<Int, Film> {
        return try{
            val currentPage = params.key ?:1
            Log.d("请求页面标记:","请求第${currentPage}页")

            val filmResponse = filmRepository.getFilms(currentPage)
            val prevKey = if(currentPage==1) null else currentPage-1
            val nextKey = if(filmResponse.isEmpty()) null else currentPage+1

            LoadResult.Page(
                data = filmResponse,
                prevKey = prevKey,
                nextKey = nextKey
            )
        }catch(e:Exception){
            if (e is IOException) {
                Log.d("测试错误数据", "-------连接失败")
            }
            Log.d("测试错误数据", "-------${e.message}")
            LoadResult.Error(throwable = e)
        }
    }
    override fun getRefreshKey(state: PagingState<Int, Film>): Int? {
        return state.anchorPosition
    }
}

5.定义视图模型层ViewModel

class MainViewModel: ViewModel() {
    private val filmRepository: FilmRepository = FilmRepositoryImp()

    fun getFilms(): Flow<PagingData<Film>> = Pager(PagingConfig(pageSize = 5)){
        FilmSource(filmRepository)
    }.flow
}

在上面的代码中配置每一页的记录数默认为5,在函数getFilms中获得一个协程流的数据封装了每页的记录。

6.结合Compose的LazyColumn定义界面层

(1)定义单独Film一行记录的界面内容

@Composable
fun FilmCard(film: Film?) {
    Card(modifier = Modifier.fillMaxSize().padding(2.dp),
        elevation = CardDefaults.cardElevation(5.dp),
        colors = CardDefaults.cardColors(containerColor = Color.Black)){
        Column{
            Row(modifier = Modifier.fillMaxSize()){
                AsyncImage(model = "${film?.poster}",
                    contentDescription = "${film?.name}")
                Column{
                    Text("${film?.name}",fontSize = 18.sp,color = Color.White)
                    Text("导演:${film?.directors}",fontSize = 14.sp,color = Color.Green)
                    Text("演员:${film?.actors}", fontSize = 14.sp,color = Color.White)
                }
            }
            Text("${film?.intro?.subSequence(0,50)} ...",fontSize = 14.sp,color=Color.Green)
            Row(horizontalArrangement = Arrangement.End,
                modifier = Modifier.fillMaxSize()){
                Text("More",fontSize=12.sp)
                IconButton(onClick ={}){
                    Icon(imageVector = Icons.Default.MoreVert,tint=Color.Green,contentDescription = "更多...")
                }
            }
        }
    }
}

(2)定义列表

@Composable
fun FilmScreen(mainViewModel: MainViewModel) {
    val films:LazyPagingItems<Film> = mainViewModel.getFilms().collectAsLazyPagingItems()
    val TAG = "加载状态"
    Column(horizontalAlignment = Alignment.CenterHorizontally,
        modifier = Modifier.background(Color.White)){
        LazyColumn{
            items(films.itemCount){
                FilmCard(films[it])
            }
        }
    }
}

7.定义主活动MainActivity

class MainActivity : ComponentActivity() {
    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)
        setContent {
            val mainViewModel:MainViewModel = viewModel()
            Ch11_DemoTheme {
                // A surface container using the 'background' color from the theme
                Surface(
                    modifier = Modifier.fillMaxSize(),
                    color = MaterialTheme.colorScheme.background
                ) {
                    FilmScreen(mainViewModel)
                }
            }
        }
    }
}

运行效果如下:
在这里插入图片描述

参考文献

Paging库概览
https://developer.android.google.cn/topic/libraries/architecture/paging/v3-overview?hl=zh-cn

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/1343746.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

详解Keras3.0 Layer API: Dropout layer

Dropout layer 图1 标准的神经网络 图2 加了Dropout临时删除部分神经元 Dropout层的作用是在神经网络中引入正则化&#xff0c;以防止过拟合。它通过随机丢弃一部分神经元&#xff08;如图2&#xff09;的输出来减少模型对训练数据的依赖性。这样可以提高模型的泛化能力&#x…

激活Windows过程及报错解决: 0x803f7001 在运行Microsoft Windows 非核心版本的计算机上, 运行“ slui.exe 0x2a 0x803f7001 “以显示错误文本

激活Windows过程及报错问题解决: 0x803f7001 在运行Microsoft Windows 非核心版本的计算机上&#xff0c;运行“ slui.exe 0x2a 0x803f7001 “以显示错误文本。 前言 最近在激活Windows过程中&#xff0c;遇到了报错: 0x803f7001 在运行Microsoft Windows 非核心版本的计算机上…

iS-RPM2023.2.0.0新版本发布

引言 经过不断努力和精心打磨,我们带着全新版本的RPM产品与大家见面啦!本次更新将为广大流程分析师和质量管理员们提供更深入、更准确的洞察力,以帮助大家在数据驱动的决策中取得更卓越的成果。然而,让海量数据转化为可用的见解并不是一项容易的任务。我们理解数据分析师们…

工业4G 物联网网关——机房动环监控系统应用方案介绍

机房动环监控系统是什么&#xff1f;机房动环监控系统的全称为机房动力环境监控系统&#xff0c;是一套安装在机房内的监控系统&#xff0c;可以对分散在机房各处的独立动力设备、环境和安防进行实时监测&#xff0c;统计和分析处理相关数据&#xff0c;第一时间侦测到故障发生…

万界星空科技车间生产管理系统解决方案

车间管理系统解决方案:   &#xff08;一&#xff09;车间生产计划管理解决方案   车间管理系统解决方案对于一般的生产计划&#xff0c;需完成编制、审批、下达、执行、完工等操作&#xff0c;车间管理系统解决方案立足于减少中间环节浪费&#xff0c;节约成本&#xff0c…

小米电脑管家 - 手机平板电脑家居互联

系列文章目录 前言 联想电脑安装小米电脑管家实现设备互联 如图&#xff0c;将 小米平板 5 Pro 作为联想笔记本 GeekPro 5000 &#xff08;这垃圾电脑&#xff09;的副屏。 可以在小米平板控制笔记本&#xff0c;如图所示 一、官方使用手册 参考&#xff1a;小米电脑管家帮助 …

WebGL以及wasm的介绍以及简单应用

简介 下面主要介绍了WebGL和wasm,是除了html,css,js以外Web标准所支持的另外两个大件 前者实现复杂的图形处理,后者提供高效的代码迁移以及代码执行效率 WebGL 简介 首先,浏览器里的游戏是怎么做到这种交互又显示不同的画面的? 试想用我们的前端三件套实现一下.好像可以…

clickhouse连接工具dbeaver

地址 地址&#xff1a; Download | DBeaver Community 安装 表引擎 表引擎之TinyLog 以列文件的形式保存在磁盘上&#xff0c;不支持索引&#xff0c;没有并发控制。一般保存少量数据的小表&#xff0c; 生产环境上作用有限&#xff0c;多用于平时练习测试用。 内存引擎&am…

【C++】Ubuntu编译filezilla client

在新版Ubuntu 22.04.3 LTS上编译filezilla client成功&#xff0c;shell命令如下&#xff1a; sudo apt-get install libfilezilla-dev libwxbase3.0-dev gnutls-dev libdbus-1-dev sudo apt-get install libwxgtk3.0-gtk3-dev sudo apt-get install libgtk-3-dev sudo apt-ge…

VSCode Python开发环境配置

目录 1 插件安装2 Debug和测试配置常见问题 1 插件安装 1.1 基础编译插件&#xff0c;Python、Pylance 1.2 修改语言服务器类型&#xff0c;进入用户配置页面搜索Python: Language Server&#xff0c;选择Pylance&#xff08;一定要修改可以提供很多语法提示&#xff09; 1…

初识智慧城市

文章目录 智慧家居 智慧社区 智慧交通 智慧医疗 智慧教育 智慧旅游 智慧农业 智慧安防 智慧家居 利用智能语音、智能交互等技术,实现用户对家居系统各设备的远程操控和能控制如开关窗帘(窗户)、操控家用电器和照明系统、打扫卫生等操作。利用计算机视觉等技术,对被照看…

3d光学轮廓仪测微光学器件应用及其重要意义

微光学器件是光学器件的重要分支&#xff0c;为光学通信、光传感、光计算等领域的发展提供重要支撑。微光学器件具有尺寸小、功耗低、低成本等优势&#xff0c;可以于电子器件集成&#xff0c;实现更高效的数据传输和信号处理。未来&#xff0c;随着微纳加工技术的进一步发展&a…

交换两个数字的三种方法-LeetCode做题总结 344

344. 反转字符串 题解Java知识点交换两个数字的三种方法1、temp2、异或3、 题解 class Solution {public void reverseString(char[] s) {char temp;for(int i0,js.length-1; i<j; i,j--) {temp s[i];s[i] s[j];s[j] temp;}} }Java知识点 交换两个数字的三种方法 1、t…

shell打印粉色小心心、颜文字心心

#!/bin/bash # *********************************************************# # # # * Author : 白嫖一茶 # # * QQ邮箱址 : 2534824121qq.com # #…

【算法与数据结构】435、LeetCode无重叠区间

文章目录 一、题目二、解法三、完整代码 所有的LeetCode题解索引&#xff0c;可以看这篇文章——【算法和数据结构】LeetCode题解。 一、题目 二、解法 思路分析&#xff1a;思路和【算法与数据结构】452、LeetCode用最少数量的箭引爆气球类似&#xff0c;也是排序找重叠区间。…

前端算法之双指针

双指针 分类 快慢指针&#xff08;同向&#xff09;对向、背向指针 例子&#xff1a;最接近的三数之和例子&#xff1a;通过删除字母匹配到字典里最长单词 双指针 双指针是一种编程技术&#xff0c;通常用于解决数组或链表的问题。 双指针法使用两个指针&#xff08;通常称为…

【Shell编程练习】监控内存和磁盘容量,小于给定值时报警

系列文章目录 输出Hello World 通过位置变量创建 Linux 系统账户及密码 系列文章目录分析代码实现运行结果 分析 对于磁盘容量&#xff0c;可以使用df命令查看指定指定分区的磁盘使用情况。比如 然后我们需要从这段输出中提取我们想要的信息。在这里就是Available字段的值。…

Vue3-31-路由-RouterView的name属性的作用

作用描述 <router-view> 标签是用来渲染路由对应的组件的位置&#xff1b; 默认情况下&#xff0c;一个路由是只对应一个组件的。 但是&#xff0c;可以通过给 <router-view> 指定 name 属性的方式&#xff0c;实现同时渲染多个组件的效果。 这也叫做 命名视图。 注…

【网络安全 | Misc】Training-Stegano-1

该题考察winhex工具使用 打开文件&#xff1a; 使用StegSolve并不能获取有效信息 使用winhex得到flag steganoI

RabbitMQ消息存储JSON格式反序列化

如果发送消息消息体为实体类对象数据&#xff0c;交换机接收消息经由路由键发送给队列。需要实现数据反序列化操作。实现JSON格式的反序列化操作 Rabbitmq的反序列化接口 MessageConverter&#xff0c;它的实现类有 Jackson2JsonMessageConverter的反序列化实现类&#xff0c…