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

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

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

如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈qq邮箱809451989@qq.com,一经查实,立即删除!

相关文章

YBM41567/4A 20V1.0A线性锂电池充电管理芯片

YBM41567/4A 20V1.0A线性锂电池充电管理芯片 概述&#xff1a; YB4156/7/4A是一款狸电池充电管理芯片&#xff0c;集成涓流、恒流、恒压三段式线性充电管理&#xff0c;符合锂电池安全充电规范。充电输入耐压高达24V,充电电流高至1.0A,可通过片外电阻配置。YB4156/7/4A集成防…

{MySQL} 数据库约束 表的关系 新增删除 修改 查询

提示&#xff1a;文章写完后&#xff0c;目录可以自动生成&#xff0c;如何生成可参考右边的帮助文档 文章目录 前言一、数据库约束1.1约束类型&#xff1a;1.2 NULL约束1.3unique 唯一约束1.4 DEFAULT&#xff1a;默认值约束1.5 PRIMARY KEY&#xff1a;主键约束1.6 FOREIGN K…

C++ 类和对象 (上)

类的引入&#xff1a; 由于C语言是面向过程的编程语言&#xff0c;我们在完成一件事的时候通常习惯将一件事拆分成一个一个小过程来实现&#xff0c;而到了C就习惯将一件事分成不同的模块&#xff0c;交给不同的对象来处理&#xff0c;每一个对象中承载着数据类型和函数。 &am…

PO 发布SAP SProxy->外围系统 WebService

通信概览图 外围系统与PO、SAP的请求响应通信过程大致可以用下图描述 &#xff08;个人整理所得&#xff0c;可能有误&#xff0c;欢迎指正&#xff09; 1. 前期准备 1.1 外围系统提供WebService接口 以A系统的RFC发布WebService接口 RFC发布WebService接口 获取到WSDL地…

【操作系统】不同操作系统内核架构分析

一、内核架构与操作系统性能之间的关系的分析 1. 适用性和专业化&#xff1a; 不同的内核架构往往会有不同的设计目标和优化点。例如&#xff0c;实时操作系统&#xff08;RTOS&#xff09;和通用操作系统&#xff08;像Linux或Windows&#xff09;在设计时就有不同的重点&am…

ESP32:整合存储配网信息和MQTT笔记

文章目录 1.给LED和KEY的所用IO增加配置项1.1 增加配置文件1.2 修改相应的c源码 2. 把mqtt\tcp的工程整合到一起2.1 在何处调用 mqtt_app_start() 3. 测试MQTT4. 完整的工程源码 有一段时间没有玩ESP32&#xff0c;很多知识点都忘记了。今天测试一下MQTT&#xff0c;做个笔记。…

循环冗余效验码的计算方法

循环冗余效验码的计算方法 G&#xff08;x&#xff09;&#xff1a; 在了解计算方法之前我们首先要明白G&#xff08;x&#xff09;表明的意思&#xff0c;这一步非常重要&#xff01; 例如&#xff0c;G&#xff08;x&#xff09; x^3 x^2 1 &#xff0c;该式子表明的编…

【每天一个早下班技巧】NPM发包流程

发包流程 step1&#xff1a;设置包发布地址 参考资料 // 1.在package.json中设置发布地址 "publishConfig":{"registry":"http://registry.npm.xxx.com" }//2.设置别名 alias ynpm"npm --registryhttp://registry.npm.xxx.com" ynp…

手把手教你配置Jenkins自动化邮件通知

完成基于Jenkins的持续集成部署后&#xff0c;自动化测试执行后&#xff0c;测试结果需要通知到相关人员&#xff0c;除了钉钉通知外我们还可以通过Email通知到对应负责人&#xff0c;这里记录一下测试结果通过Jenkins邮件通知的配置与部署 01、安装插件 方法1&#xff1a; 进…

【neo4j】neo4j的安装与使用

【neo4j】neo4j的安装与使用 安装java https://www.oracle.com/java/technologies/downloads/ 按照步骤安装即可 配置环境变量 在系统变量中添加 path变量中添加 安装neo4j https://neo4j.com/deployment-center/ 下载后&#xff0c;在指定位置解压缩 与java相同&#…

马斯克谈六西格玛:管理质量与火箭科学如何相辅相成

如果你问我&#xff0c;作为一个有志于送人类上火星的家伙&#xff0c;六西格玛管理有没有用&#xff1f;我会说&#xff0c;如果把火箭控制的精度与六西格玛得到的生产质量相比&#xff0c;你会发现两者都追求同一件事&#xff1a;接近零缺陷的完美。 六西格玛不只是一组工具…

如何在 NAS 上安装 ONLYOFFICE 文档?

文章作者&#xff1a;ajun 导览 ONLYOFFICE 文档 是一款开源办公套件&#xff0c;其是包含文本文档、电子表格、演示文稿、表单、PDF 查看器和转换工具的协作性编辑工具。它高度兼容微软 Office 格式&#xff0c;包括 .docx、.xlsx 、.pptx 、pdf等文件格式&#xff0c;并支持…

wireshark access/trunk/hybrid报文分析

1&#xff0c;access接口 发送带vlan的报文 wireshark交换机配置 [Huawei-GigabitEthernet0/0/1] [Huawei-GigabitEthernet0/0/1]port link-type access [Huawei-GigabitEthernet0/0/1]port default vlan 100 [Huawei-GigabitEthernet0/0/2]port link-type access [Huawei-Gig…

华为---USG6000V防火墙web基本配置示例

目录 1. 实验要求 2. 配置思路 3. 网络拓扑图 4. USG6000V防火墙端口和各终端相关配置 5. 在USG6000V防火墙web管理界面创建区域和添加相应端口 6. 给USG6000V防火墙端口配置IP地址 7. 配置通行策略 8. 测试验证 8.1 逐个删除策略&#xff0c;再看各区域终端通信情况 …

SpringMVC之视图和RESTful

学习的最大理由是想摆脱平庸&#xff0c;早一天就多一份人生的精彩&#xff1b;迟一天就多一天平庸的困扰。各位小伙伴&#xff0c;如果您&#xff1a; 想系统/深入学习某技术知识点… 一个人摸索学习很难坚持&#xff0c;想组团高效学习… 想写博客但无从下手&#xff0c;急需…

Java注解之@Autowired,一文掌握@Autowired注解知识(3)

&#x1f3c6;作者简介&#xff0c;普修罗双战士&#xff0c;一直追求不断学习和成长&#xff0c;在技术的道路上持续探索和实践。 &#x1f3c6;多年互联网行业从业经验&#xff0c;历任核心研发工程师&#xff0c;项目技术负责人。 &#x1f389;欢迎 &#x1f44d;点赞✍评论…

k8s搭建(四、k8s集群创建)

天行健&#xff0c;君子以自强不息&#xff1b;地势坤&#xff0c;君子以厚德载物。 每个人都有惰性&#xff0c;但不断学习是好好生活的根本&#xff0c;共勉&#xff01; 文章均为学习整理笔记&#xff0c;分享记录为主&#xff0c;如有错误请指正&#xff0c;共同学习进步。…

05-C++ 类和对象-继承

类与对象-03 继承与派生 1. 继承的概念 c最重要的特征是代码重用&#xff0c;通过继承机制可以利用已有的数据类型&#xff0c;来定义新的数据类型&#xff0c;新的类不仅拥有旧类的成员&#xff0c;还拥有新定义的成员。 一个 B 类继承于 A 类&#xff0c;或称从类 A 派生…

QCheckBoxQRadioBoxQComboBoxQSlider

QCheckBox QCheckBox 是 Qt 框架中一个常用的控件&#xff0c;用于创建一个可以勾选或取消勾选的复选框。它通常用于表示选项的开/关状态 autoExclusive 是一个与单选按钮&#xff08;QRadioButton&#xff09;相关的属性。这个属性决定了同一个父窗口下的单选按钮是否自动形成…

【C++干货铺】STL中set和map的介绍和使用

个人主页点击直达&#xff1a;小白不是程序媛 C系列专栏&#xff1a;C干货铺 代码仓库&#xff1a;Gitee 目录 序列式容器 关联式容器 键值对 树形结构的关联式容器 set set的介绍 set的使用 set的模板参数列表 set的构造 ​编辑 set的容量 set的删除和查找 mult…