Android笔记(十五):JetPack Compose的附带效应(二)-produceState和derivedStateOf

news2025/1/15 12:51:43

在本笔记中,将结合实例介绍produceState和derivedStateOf两个可组合函数。它们分别实现状态的转换。
(1)produceState将非Compose状态转换虫Compose状态
(2)derivedStateOf将多个状态转换成其他状态。

一、produceState

produceState可将非 Compose 状态转换为 Compose 状态,它会在没有定义数据源的情况下随时间生成值。produceState 会启动一个协程,该协程将作用域限定为可将值推送到返回的 State 的组合。

produceState可组合函数返回一个可观察的快照状态,当produceState函数进入组合会启动producer的操作,创建一个状态值。如果key1和key2的值发生了变化,正在运行的producer会被取消并重新加载创建新的状态源。producer使用ProducerStateScope.value为返回的状态设置新的值:

如果ProducerStateScope.value混淆了返回状态的新旧值,使得ProducerStateScope.value返回的状态的值仍为状态旧值,则不会观察到任何的更改。
如果在频繁设置多个新值,那么观察者只会观察到最新的值。

在下列中访问并显示网络中的在线图片,因为网络请求到缓存到本地存在时间差,因此考虑将这样的处理使用produceState来完成。只要状态值更新,就刷新界面。
(1)定义请求结果的类
请求资源结果的类OpResult是密封类,封装了两种处理方式:请求图片资源成功和请求图片资源失败两种子类。

sealed class OpResult<T>(){
    object Loading:OpResult<ImageBitmap>()
    object Error:OpResult<ImageBitmap>()
    data class Success(val image:ImageBitmap):OpResult<ImageBitmap>()
}

(2)定义图片库ImageRepository

class ImageRepository  constructor(context:Context){
    val context:Context = context
    var id = 0
    //https://wome.com网站不存在,网站的图片链接均为假,调试时请自行设置
    val imageLst =listOf("https://some.com.1.jpg",
            "https://some.com.2.jpg",
            "https://some.com.3.jpg",
            "https://some.com.4.jpg")
    fun findByUrl(url:String) =  imageLst.indexOf(url)
    //请求下一个图片资源的url字符串 
    suspend fun next():String{
        id = (id+1)%imageLst.size
        return imageLst.get(id)
    }

	//请求上一个图片资源的url字符串 
    suspend fun prev():String{
        id = (id-1+imageLst.size)%imageLst.size
        return imageLst.get(id)
    }
    
    /加载在线图片资源,返回ImageBitmap
    suspend fun loadImageByUrl(url:String): ImageBitmap {
        //请求在线图片
        val request = ImageRequest.Builder(context).data(url).build()
        val imageLoader by lazy{ ImageLoader(context) }
        return (imageLoader.execute(request).drawable as BitmapDrawable)
               .bitmap.asImageBitmap().apply{
                //延迟两秒加载图片
                delay(1000)
            }
    }
}

因为要访问网络在线图片。因此,需要在应用的AndroidManifest.xml中设置网络访问权限:

<uses-permission android:name="android.permission.INTERNET" />

在上述请求网络在线的图片资源,需要coil库,因此在项目模块的build.gradle.kt中设置为:

implementation(“io.coil-kt:coil-compose:2.4.0”)

(3)定义可组合函数请求在线图片

/**
 * 将非Composable状态转换成Composable状态
 * @param imageRepository ImageRepository 图片仓库
 * @return State<OpResult<ImageBitmap>>
 */
@Composable
fun loadNetworkImages(imageRepository: ImageRepository):State<OpResult<ImageBitmap>>{
    return produceState<OpResult<ImageBitmap>>(initialValue = OpResult.Loading){
                            for(i in 0 until imageRepository.imageLst.size) {
                                var image = imageRepository.loadImageByUrl(imageRepository.imageLst[i])
                                value = if (image == null) {
                                    OpResult.Error
                                } else {
                                    OpResult.Success(image)
                                }
                            }
    }
}

在此处,返回一个包含请求资源结果的状态,根据循环,使得图片列表的索引值发生变换,从图片仓库对象imageRepository中要加载的图片。如果图片请求成功,将image图片封装到状态的值为OpResult.Success(image)中。

(4)定义界面ImageScreen

@Composable
fun ImageScreen(imageRepository: ImageRepository) {
    val result by  loadNetworkImages(imageRepository = imageRepository) //从图片仓库中请求资源,返回包含请求结果的状态
    Box(
        modifier = Modifier.fillMaxSize().background(Color.Black),
        contentAlignment = Alignment.Center
    ) {
        Column(
            modifier = Modifier.fillMaxWidth().height(300.dp),
            verticalArrangement = Arrangement.Center,
            horizontalAlignment = Alignment.CenterHorizontally
        ) {
            when(result) {
                is OpResult.Success -> {
                    Image(modifier = Modifier.size(400.dp,300.dp),
                        bitmap = (result as OpResult.Success).image,
                        contentDescription = null
                    )
                }
                is OpResult.Error -> {
                    Image(
                        imageVector = Icons.Filled.Warning,
                        contentDescription = null,
                        modifier = Modifier.size(200.dp, 200.dp)
                    )
                }
                else -> {//等待加载图片时,显示圆形进度条
                    CircularProgressIndicator()
                }
            }
        }
    }
}

(5)定义主活动MainActivity

class MainActivity : ComponentActivity() {
    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)
        val imageRepository = ImageRepository(this)
        setContent {
            Ch06_DemoTheme {
                // A surface container using the 'background' color from the theme
                Surface(
                    modifier = Modifier.fillMaxSize(),
                    color = MaterialTheme.colorScheme.background
                ) {
                    ImageScreen(imageRepository)
                }
            }
        }
    }
}

运行结果就是图片动态显示直至最后一张图片为止,如下图所示:
在这里插入图片描述
模拟器中图片资源来自于网络。如有侵权,会主动删除。

二、derivedStateOf

derivedStateOf函数将一个或多个状态对象转换为其他状态,使用此函数可确保仅当计算中使用的状态之一发生变化时才会进行计算。首先,从一个简单的例子来初步了解:

@Composable
fun DisplayScreen(){
    val yearState = remember{mutableStateOf(2023)}
    val monthState = remember{mutableStateOf(11)}

    val calendarState = remember {
        derivedStateOf {
            "${yearState.value}年-${monthState.value}月"
        }
    }

    Box(contentAlignment = Alignment.Center){
        Column(modifier = Modifier.fillMaxWidth(),
            horizontalAlignment = Alignment.CenterHorizontally){
            Text("${calendarState.value}",fontSize=30.sp)
            Button(onClick ={
                monthState.value = (monthState.value)%12
                if(monthState.value == 0 ){
                    yearState.value++
                }
                monthState.value++
            }){
                Text("修改月份",fontSize = 24.sp)
            }
        }

    }
}

在上述的代码中,定义了两个状态值分别为yearState和monthState,通过derivedStateOf函数将两个状态的值连接生成一个新的状态CalendarState,而这个CalendarState的值为字符串。通过点击按钮,修改monthState和yearState的值,在界面中显示的CalendarState的值也发生相应的变化。

参考文献

Compose 中的附带效应
https://developer.android.google.cn/jetpack/compose/side-effects?hl=zh-cn

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

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

相关文章

2023年金融信创行业研究报告

第一章 行业概况 1.1 定义 金融信创是指在金融行业中应用的信息技术&#xff0c;特别是那些涉及到金融IT基础设施、基础软件、应用软件和信息安全等方面的技术和产品。这一概念源于更广泛的“信创 (信息技术应用创新)”&#xff0c;即通过中国国产信息技术替换海外信息技术&a…

全球最大生产基地已投产,百年京西借智能悬架谋「新生」

受相关等爆款车型的高配置率及销量带动&#xff0c;空气悬架市场热度不减。 比如&#xff0c;理想在今年的理想魔毯空气悬架技术日上宣布&#xff0c;搭载空气悬架的车型累计交付已突破20万辆&#xff0c;在所有已交付的L9、L8、L7中&#xff0c;配备空气悬架的比例达93%。 作…

php通过curl方式发送接受xml数据

目录 1、php通过curl方式发送xml数据 2、php通过file_get_contents接受curl方式发送xml数据 1、php通过curl方式发送xml数据 <?php function sendXmlData($url, $xmlData) {$ch curl_init();curl_setopt($ch, CURLOPT_URL, $url);curl_setopt($ch, CURLOPT_RETURNTRANSFE…

亚马逊云科技向量数据库助力生成式AI成功落地实践探秘(二)

向量数据库选择哪种近似搜索算法&#xff0c;选择合适的集群规模以及集群设置调优对于知识库的读写性能也十分关键&#xff0c;主要需要考虑以下几个方面&#xff1a; 向量数据库算法选择 在 OpenSearch 里&#xff0c;提供了两种 k-NN 的算法&#xff1a;HNSW (Hierarchical…

代码随想录算法训练营第四十四天【动态规划part06】 | 完全背包、518. 零钱兑换 II、377. 组合总和 Ⅳ

完全背包 有N件物品和一个最多能背重量为W的背包。第i件物品的重量是weight[i]&#xff0c;得到的价值是value[i] 。每件物品都有无限个&#xff08;也就是可以放入背包多次&#xff09;&#xff0c;求解将哪些物品装入背包里物品价值总和最大。 题目链接&#xff1a; 题目页…

android keylayout键值适配

1、通过getevent打印查看当前keyevent数字对应事件和物理码 2、dumpsys input 查看输入事件对应的 KeyLayoutFile: /system/usr/keylayout/Vendor_6080_Product_8060.kl 3、通过物理码修改键值映射&#xff0c;修改/system/usr/keylayout/目录下的文件

嵌入式系统在工业自动化中的智能化和自适应控制

嵌入式系统在工业自动化中扮演着实现智能化和自适应控制的重要角色。通过集成先进的算法和人工智能技术&#xff0c;嵌入式系统能够实现对生产过程的智能监控、分析、决策和调整&#xff0c;以提高生产线的效率、质量和稳定性。下面将详细介绍嵌入式系统在工业自动化中智能化和…

​飞凌嵌入式FCU2601网关,为工商业储能EMS注入智慧的力量

一、火热的储能行业&#xff0c;寻求新的市场机会 最近一段时间以来&#xff0c;世界储能大会、上海储能展、能源电子产业发展大会等多个储能相关论坛和展览密集登场&#xff0c;即使“内卷”已成为了业内讨论的热词&#xff0c;但寻求新的市场机会仍然是行业共识&#xff0c;…

“强标”即将迎来正式发布,智能汽车准备好了吗?

汽车越来越智能&#xff0c;这让汽车安全在传统的功能安全之外又多了一项新的命题&#xff1a;信息安全。广州国际车展开幕前&#xff0c;有媒体发起了一项调研&#xff0c;调研结果显示43%的用户将“信息安全”和品牌、外观、价格、续航等一起纳入了购买智能汽车的首要考虑因素…

虚拟机系列:vmware和Oracle VM VirtualBox虚拟机的区别,简述哪一个更适合我?以及相互转换

一. VMware和Oracle VM VirtualBox虚拟机的区别主要体现在以下几个方面: 首先两种软件的安装使用教程如下: VMware ESXI 安装使用教程 Oracle VM VirtualBox安装使用教程 商业模式:VMware是一家商业公司,而Oracle VM VirtualBox是开源软件; 功能:VMware拥有更多的功能和…

工业以太网交换机未来发展中的几个趋势

随着工业自动化不断发展和智能制造的推进&#xff0c;工业以太网交换机在未来的应用中将面临更多的发展机遇和挑战。在工业以太网交换机的未来发展中&#xff0c;有几个方面将成为趋势。 网络虚拟化 随着工业自动化系统规模的不断扩展&#xff0c;网络虚拟化将成为未来的发展方…

webshell之基于框架免杀

thinkphp array_map_recursive函数 array_map_recursive函数分析 这里存在一个call_user_func命令执行函数 免杀效果 B函数 免杀效果 B函数分析 exec函数分析 在exec函数用存在有个类调用&#xff0c;且所有的参数都可控 smarty_php_tag函数 免杀效果 smarty_php_tag函数分析…

第一个Mybatis项目

&#xff08;一&#xff09;为什么要用Mybatis? &#xff08;1&#xff09;Mybatis对比JDBC而言&#xff0c;sql&#xff08;单独写在xml的配置文件中&#xff09;和java编码分开&#xff0c;功能边界清晰&#xff0c;一个专注业务&#xff0c;一个专注数据。 &#xff08;2&…

网站监控是什么

在当今高度互联的世界中&#xff0c;网站已成为企业和个人成功的关键因素。无论是提供产品或服务&#xff0c;还是建立品牌形象&#xff0c;网站都是不可或缺的工具。然而&#xff0c;随着互联网用户对访问速度和用户体验的高要求&#xff0c;保持网站的稳定性和可用性变得至关…

C语言如何封装CPP代码的接口

为什么要用C语言封装CPP代码&#xff1f; C不是兼容C语言的吗&#xff1f; 是的&#xff0c;如果你只是自己开发程序&#xff0c;只用一种语言的话&#xff0c;就没有必要进行封装。如果你开发的是一个C的.so库&#xff0c;你希望将这个库提供给其他语言使用的话&#xff0c;…

零代码AppLink平台触发事件组件

AppLink平台组件组成 AppLink平台组件分成三个板块触发事件组件、基础组件和数据连接器 触发组件下有三个组件&#xff0c;分别是Webhook、定时器、高级Webhook&#xff0c;那他们在AppLink平台里的原理、触发动作以及怎么使用呢&#xff1f;接下来为大家演示下。 Webhook是…

RabbitMQ 消息队列编程

安装与配置 安装 RabbitMQ 读者可以在 RabbitMQ 官方文档中找到完整的安装教程&#xff1a;Downloading and Installing RabbitMQ — RabbitMQ 本文使用 Docker 的方式部署。 RabbitMQ 社区镜像列表&#xff1a;https://hub.docker.com/_/rabbitmq 创建目录用于映射存储卷…

ssm+vue的企业文档管理系统(有报告)。Javaee项目,ssm vue前后端分离项目。

演示视频&#xff1a; ssmvue的企业文档管理系统&#xff08;有报告&#xff09;。Javaee项目&#xff0c;ssm vue前后端分离项目。 项目介绍&#xff1a; 采用M&#xff08;model&#xff09;V&#xff08;view&#xff09;C&#xff08;controller&#xff09;三层体系结构&…

Mongodb6.0+,使用mongosh连接数据库

介绍: MongoDB的Shell工具mongosh是一个全功能的JavaScript和Node.js的14.x REPL与MongoDB的部署交互环境。 命令: 连接数据库:mongosh mongodb://127.0.0.1:27017 重置密码找到配置文件,将用户密码认证关闭: 连接数据库 找到admin库,然后看一下用户 删除用户重建, …

【数据结构/C++】线性表_顺序表的基本操作

#include <iostream> using namespace std; #define MaxSize 10 // 1. 顺序表 // 静态分配 typedef struct {int data[MaxSize];int length; // 当前长度 } SqList; // 静态分配初始化顺序表 void InitList(SqList &L) {for (int i 0; i < MaxSize; i){L.data[i]…