Android Compose list 下拉刷新、上拉加载更多

前言:本文使用UI框架Compose +Paging+Swiperefresh实现列表的下拉刷新,上拉加载更多功能

一、添加相关库包括viewModel ,Paging,Swiperefresh的Compose库


    implementation ("androidx.emoji2:emoji2:1.3.0")
    //network & serialization
    implementation ("com.google.code.gson:gson:2.9.0")
    implementation ("com.squareup.retrofit2:converter-gson:2.9.0")
    implementation ("com.squareup.retrofit2:retrofit:2.9.0")//
    implementation ("com.squareup.okhttp3:logging-interceptor:4.9.3")
    //swiperefresh 的compose 版本
    implementation ("com.google.accompanist:accompanist-swiperefresh:0.30.1")
    // paging 3 的compose 版本
    implementation ("androidx.paging:paging-compose:1.0.0-alpha18")
    //这个可以在Compose中得到viewmodel
    implementation ("androidx.lifecycle:lifecycle-viewmodel-compose:2.6.1")
    //coil Compose 图片加载库
    implementation ("io.coil-kt:coil-compose:2.0.0-rc01")

二、MainActivity


class MainActivity : ComponentActivity() {
    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)
        setContent {
            ComposeDemoTheme {
                // A surface container using the 'background' color from the theme
                Surface(
                    modifier = Modifier.fillMaxSize(),
                    color = MaterialTheme.colorScheme.background
                ) {
                    Greeting("Android")
                }
            }
        }
    }
}

@Composable
fun Greeting(name: String, modifier: Modifier = Modifier) {
    ListContent()
}

@Preview(showBackground = true)
@Composable
fun GreetingPreview() {
    ComposeDemoTheme {
        Greeting("Android")
    }
}


@Composable
fun ListContent(){
    val viewModel:GithubViewModel= viewModel()
    val lazyPagingItems=viewModel.repositorPager.collectAsLazyPagingItems()
    val state:LazyListState= rememberLazyListState()
    SwipeRefresh(state =rememberSwipeRefreshState(lazyPagingItems.loadState.refresh is LoadState.Loading&&lazyPagingItems.itemCount > 0) ,
        onRefresh = { lazyPagingItems.refresh() }) {
        LazyColumn(state =state,
            contentPadding = PaddingValues(10.dp),
            verticalArrangement = Arrangement.SpaceEvenly){
               items(items=lazyPagingItems){item->
                   item?.let {
                       RepositorCard(it)
                   }
               }
               if(lazyPagingItems.loadState.append is LoadState.Loading){
                    item {
                        Box(modifier = Modifier
                            .fillMaxWidth()
                            .height(50.dp)){
                            CircularProgressIndicator(modifier = Modifier.align(Alignment.Center))
                        }
                    }
                }

        }
        if(lazyPagingItems.loadState.refresh is LoadState.Loading){
            if(lazyPagingItems.itemCount==0){
                Box(modifier = Modifier.fillMaxSize()) {
                    CircularProgressIndicator(modifier = Modifier.align(alignment = Alignment.Center))
                }
            }
        }else if(lazyPagingItems.loadState.refresh is LoadState.Error){
            //加载失败的错误页面
            Box(modifier = Modifier.fillMaxSize()) {
                Button(modifier = Modifier.align(alignment = Alignment.Center),
                    onClick = { lazyPagingItems.refresh() }) {
                    Text(text = "加载失败!请重试")
                }
            }
        }
    }
}

 @Composable
fun RepositorCard(repositorItem: RepositorItem) {
    Card(modifier = Modifier
        .fillMaxWidth()
        .padding(8.dp)) {
        Row(modifier = Modifier
            .fillMaxWidth()
            .height(88.dp)) {
            Spacer(modifier = Modifier.width(10.dp))
            Surface(shape = CircleShape, modifier = Modifier
                .size(66.dp)
                .align(Alignment.CenterVertically)) {
                AsyncImage(model = repositorItem.owner.avatar_url,
                    contentDescription = "",
                    contentScale = ContentScale.Crop)
            }

            Spacer(modifier = Modifier.width(15.dp))
            Column(modifier = Modifier.fillMaxWidth()) {
                Spacer(modifier = Modifier.height(8.dp))
                Text(text = repositorItem.name,
                   )
                Text(text = repositorItem.full_name, style = MaterialTheme.typography.bodyMedium)
            }
        }
    }
}

三、MyPagingSource

class MyPagingSource(
    val githubService: GithubService = getGithubService(),
    val words: String,
) : PagingSource<Int, RepositorItem>() {

    override fun getRefreshKey(state: PagingState<Int, RepositorItem>): Int? {
        return state.anchorPosition?.let {
            val anchorPage = state.closestPageToPosition(it)
            anchorPage?.prevKey?.plus(1) ?: anchorPage?.nextKey?.minus(1)
        }
    }

    override suspend fun load(params: LoadParams<Int>): LoadResult<Int, RepositorItem> {
        try {
            val nextPage: Int = params.key ?: 1
            val repositorRst = githubService.searchRepositors(words, nextPage, 20)
            return LoadResult.Page(
                data = repositorRst.items,
                prevKey = if (nextPage == 1) null else nextPage - 1,
                nextKey = if (repositorRst.items.isEmpty()) null else nextPage + 1
            )
        }catch (e:Exception){
            return LoadResult.Error(e)
        }
    }

四、GithubViewModel

class GithubViewModel:ViewModel() {
    val repositorPager = Pager(config = PagingConfig(pageSize = 6)){
        MyPagingSource(getGithubService(),"compose")
    }.flow.cachedIn(viewModelScope)

}

Demo下载:https://download.csdn.net/download/ange_li/90152118

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

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

相关文章

基于base32的兑换码算法(思路)

base32编码指的是基于32个可打印字符对任意字节数据进行编码&#xff1a;大写字母A-Z以及数字2-7。 兑换码要求:长度为10个字符 如果将这32个字符依次放到一个base数组中&#xff0c;那么最大的下标就是31。我们将要编码的任意字节数据按照五个bit为一组进行划分&#xff0c;…

前端开发环境(vue)

1. 安装nvm管理nodejs的版本 1. 配置nvm 2. 用npm安装nodejs,选则nodejs版本,这是js的运行环境 3 . 安装npm,这是前端的包管理器 npm是nodejs开发的包管理器,现在下载了nodejs就默认下载npm了,绑在一块了,不用 1. npm的中央仓库 2. npm私服仓库 换库 npm config set r…

第十七章:反射+设计模式

一、反射 1. 反射(Reflection)&#xff1a;允许在程序运行状态中&#xff0c;可以获取任意类中的属性和方法&#xff0c;并且可以操作任意对象内部的属 性和方法&#xff0c;这种动态获取类的信息及动态操作对象的属性和方法对应的机制称为反射机制。 2. 类对象 和 类的对象(实…

arduino继电器与电机水泵的使用

首先说一句&#xff0c;真受不了网上的教程&#xff0c;大海里捞金&#xff0c;要不上来了就讲原理&#xff0c;怎么具体使用一句不说&#xff0c;要么炫技来了。 继电器&#xff0c;简单来说把他当开关看&#xff0c;通过小电流控制大电流(原理去看其他视频)&#xff0c;要记…

【Java Web】Axios实现前后端数据异步交互

目录 一、Promise概述 二、Promise基本用法 三、async和await关键字 四、Axios介绍 4.1 Axios基本用法 4.2 Axios简化用法之get和post方法 五、Axios拦截器 六、跨域问题处理 一、Promise概述 axios是代替原生的ajax实现前后端数据交互的一套新解决方案&#xff0c;而…

网络编程 03:端口的定义、分类,端口映射,通过 Java 实现了 IP 和端口的信息获取

一、概述 记录时间 [2024-12-19] 前置文章&#xff1a; 网络编程 01&#xff1a;计算机网络概述&#xff0c;网络的作用&#xff0c;网络通信的要素&#xff0c;以及网络通信协议与分层模型 网络编程 02&#xff1a;IP 地址&#xff0c;IP 地址的作用、分类&#xff0c;通过 …

webdriver 反爬虫 (selenium反爬虫) 绕过

1. webdriver 反爬虫原理 爬虫程序可以借助渲染工具从动态网页中获取数据。 在这个过程中&#xff0c;“借助”其实是通过对应的浏览器驱动&#xff08;即WebDriver&#xff09;向浏览器发出指令的行为。因此&#xff0c;开发者可以根据客户端是否包含浏览器驱动这一特征来区分…

JAVA 零拷贝技术和主流中间件零拷贝技术应用

目录 介绍Java代码里面有哪些零拷贝技术java 中文件读写方式主要分为什么是FileChannelmmap实现sendfile实现 文件IO实战需求代码编写实战IOTest.java 文件上传阿里云&#xff0c;测试运行代码看耗时为啥带buffer的IO比普通IO性能高&#xff1f;BufferedInputStream为啥性能高点…

系统移植——Linux 内核顶层 Makefile 详解

一、概述 Linux Kernel网上下载的版本很多NXP等有自己对应的版本。需要从网上直接下载就可以。 二、Linux内核初次编译 编译内核之前需要先在 ubuntu 上安装 lzop 库 sudo apt-get install lzop 在 Ubuntu 中 新 建 名 为 “ alientek_linux ” 的 文 件夹 &#xff0c; …

Reactor

文章目录 正确的理解发送double free问题解决 1.把我们的reactor进行拆分2.链接管理3.Reactor的理论 listensock只需要设置_recv_cb&#xff0c;而其他sock&#xff0c;读&#xff0c;写&#xff0c;异常 所以今天写nullptr其实就不太对&#xff0c;添加为空就没办法去响应事件…

【深度学习】 零基础介绍卷积神经网络(CNN)

CNN学习 零基础介绍写个CNN最简单的代码一. 概述二. 搭建CNN1. 输入层2. 卷积层3. 激活层4. 池化层5. 全连接层6. 网络搭建小结7. 损失函数8. 梯度下降9. 反向传播10. 模型评估与正则化11. 尝试搭建自己的第一个CNN 三. 经典CNN结构四. 猫狗识别项目实践1. Paddle实现版本&…

Leetcode打卡:找到稳定山的下标

执行结果&#xff1a;通过 题目&#xff1a; 3258 找到稳定山的下标 有 n 座山排成一列&#xff0c;每座山都有一个高度。给你一个整数数组 height &#xff0c;其中 height[i] 表示第 i 座山的高度&#xff0c;再给你一个整数 threshold 。 对于下标不为 0 的一座山&#xf…

leetcode刷题日记03——javascript

题目3&#xff1a; 回文数https://leetcode.cn/problems/palindrome-number/ 给你一个整数 x &#xff0c;如果 x 是一个回文整数&#xff0c;返回 true &#xff1b;否则&#xff0c;返回 false 。 回文数是指正序&#xff08;从左向右&#xff09;和倒序&#xff08;从右向…

服务器数据恢复—RAIDZ离线硬盘数超过热备盘数导致阵列崩溃的数据恢复案例

服务器存储数据恢复环境&#xff1a; ZFS Storage 7320存储阵列中有32块硬盘。32块硬盘分为4组&#xff0c;每组8块硬盘&#xff0c;共组建了3组RAIDZ&#xff0c;每组raid都配置了热备盘。 服务器存储故障&#xff1a; 服务器存储运行过程中突然崩溃&#xff0c;排除人为误操…

Tact智能合约安全实践:TON生态系统中的常见错误

TON&#xff08;The Open Network&#xff09;以其创新特性和强大的智能合约性能&#xff0c;不断拓宽区块链技术的边界。基于早期的区块链平台&#xff08;如以太坊等&#xff09;的经验与教训&#xff0c;TON为开发者提供了一个更加高效且灵活的开发环境。其中推动这一进步的…

C进阶—指针(1)

若是阁下满意的话&#xff0c;可否一键三连呢&#xff01; 第一篇进阶指针就是先了解各种新的概念&#xff08;用法我们后面几篇再详细说&#xff01;先只介绍概念&#xff09;&#xff0c;有疑惑很正常&#xff0c;只是暂时的&#xff0c;我们一起来看看吧&#xff01; 字符指…

【Python使用】嘿马头条项目从到完整开发教程第9篇:缓存,1 缓存穿透【附代码文档】

本教程的知识点为:简介 1. 内容 2. 目标 产品效果 ToutiaoWeb虚拟机使用说明 数据库 理解ORM 作用 思考&#xff1a; 使用ORM的方式选择 数据库 SQLAlchemy操作 1 新增 2 查询 all() 数据库 分布式ID 1 方案选择 2 头条 使用雪花算法 &#xff08;代码 toutiao-backend/common/…

谷歌浏览器的扩展程序自动更新设置

谷歌浏览器是全球最受欢迎的网络浏览器之一&#xff0c;其扩展程序更是为用户提供了丰富的功能。然而&#xff0c;随着时间的推移&#xff0c;扩展程序需要更新以修复漏洞、提升性能或增加新功能。本文将详细介绍如何在Chrome中设置扩展程序的自动更新。&#xff08;本文由http…

LabVIEW与PLC点位控制及OPC通讯

在工业自动化中&#xff0c;PLC通过标准协议&#xff08;如Modbus、Ethernet/IP等&#xff09;与OPC Server进行数据交换&#xff0c;LabVIEW作为上位机通过OPC客户端读取PLC的数据并进行监控、控制与处理。通过这种方式&#xff0c;LabVIEW能够实现与PLC的实时通信&#xff0c…

在Windows Server路由和远程访问服务中启用L2TP/IPsec VPN

背景 路由和远程访问服务&#xff08;Routing and Remote Access Services&#xff0c;RRAS&#xff09;是Windows Server上的一个角色&#xff0c;包含很多功能&#xff0c;可以用来搭建VPN。然而&#xff0c;在什么也不做的初始配置中&#xff0c;它只允许PPTP协议连接。然而…