Compose事件与状态简略介绍

提示:需要对基本的compose语法有所了解`

文章目录

  • 前言
  • 一、Compose的状态是什么?
  • 二、Compose的事件是什么?与remember函数
    • 1.事件
    • 2.remember
  • 三.rememberSaveable
  • 题外话
  • 总结


前言

阅读本文需要一定compose基础,如果没有请移步Jetpack Compose入门详解(实时更新)

本文介绍Compose中状态与事件的基本概念以及规范。根据官网教程总结,如有不对请在评论区指教


一、Compose的状态是什么?

应用的“状态”是指可以随时间变化的任何值。这是一个非常宽泛的定义,从 Room 数据库到类的变量,全部涵盖在内。

所有 Android 应用都会向用户显示状态。下面是 Android 应用中的一些状态示例:

  • 聊天应用中最新收到的消息。
  • 用户的个人资料照片。
  • 在项列表中的滚动位置。
  • 乃至于你微信钱包里的余额。

下面我们通过一个例子来对状态有一个深刻的印象(虽然钱包余额已经够深刻了),对于此时此刻的我来说会使我印象深刻的就是国庆节的余额

@Composable
fun NationalDay(modifier: Modifier = Modifier) {
    val day = 0
    Text(
        text = "我们有 $day 天来庆祝国庆节!",
        modifier = modifier.padding(16.dp)
    )
}

然后我们将其作为可组合项在setContent中调用起来得到如下界面
Compose事件与状态简略介绍_第1张图片
对!今天是最后一天啦,没有时间庆祝了!
咳咳,话不扯远了,在本例中,变量 val day = 0 就是我们的一个状态,可静态状态‘val’无法修改,因此用处不大。

二、Compose的事件是什么?与remember函数

1.事件

“状态”是指可以随时间变化的任何值,例如,聊天应用最新收到的消息。但是,是什么原因导致状态更新呢?在 Android 应用中,状态会根据事件进行更新。

事件是从应用外部或内部生成的输入,例如:

  • 用户与界面互动,例如按下按钮。
  • 其他因素,例如传感器发送新值或网络响应。
  • 你用微信钱包的余额搞了个疯狂星期四,然后你的钱包余额变少了50

应用的状态说明了要在界面中显示的内容,而事件则是一种机制,可在状态发生变化时导致界面发生变化。
这里用官方图可能更清晰一点
Compose事件与状态简略介绍_第2张图片
那么正常以按下按钮为例来说,我们的例子应该变成这样

添加一个按钮,将变量day改为var类型,然后在按钮按下是将day+1

@Composable
fun NationalDay(modifier: Modifier = Modifier) {
Column(modifier = modifier.padding(16.dp)) {
    var day = 0
    Text(
        text = "我们有 $day 天来庆祝国庆节!",
        modifier = modifier.padding(16.dp)
    )
    Button(onClick = { day++ }) {
        Text(text = "加一天")
    }
    }
}

但实际上这并不会有任何效果,这是因为compose在按钮按下前已经绘制完成,当day变量发生变化时,compose并没有重组,它并不知道day已经从0变为1了,这时,我们需要使用到remember函数来帮助compose记住day变量是否发生变化。

2.remember

这个内嵌函数中文释义如下

remember 可组合内嵌函数。系统会在初始组合期间将由 remember 计算的值存储在组合中,并在重组期间一直保持存储的值。

示例如下:

import androidx.compose.runtime.remember

@Composable
fun NationalDayVal(modifier: Modifier = Modifier) {
    Column(modifier = modifier.padding(16.dp)) {
        val day: MutableState<Int> = remember { mutableStateOf(0) }
        Text(
            text = "我们有 ${day.value} 天来庆祝国庆节!",
            modifier = modifier.padding(16.dp)
        )
        Button(onClick = { day.value++ }) {
            Text(text = "加一天")
        }
    }
}

此时我们可以发现代码生效了
Compose事件与状态简略介绍_第3张图片
我们也可以通过kotlin简化上例写法,效果是一样的

@Composable
fun NationalDayVar(modifier: Modifier = Modifier) {
    Column(modifier = modifier.padding(16.dp)) {
        var day by remember { mutableStateOf(0) }
        Text(
            text = "我们有 $day 天来庆祝国庆节!",
            modifier = modifier.padding(16.dp)
        )
        Button(onClick = { day++ }) {
            Text(text = "加一天")
        }
    }
}

注意: remember 会将对象存储在组合中,而如果在重组期间未再次调用之前调用 remember 的来源位置,则会忘记对象。
为了更直观的展示我们新增一个提示UI如下

@Composable
fun  NationalDayNoticeItem(
   noticeName: String,
    onClose: () -> Unit,
    modifier: Modifier = Modifier
){
    Row(
        modifier = modifier, verticalAlignment = Alignment.CenterVertically
    ) {
        Text(
            modifier = Modifier
                .weight(1f)
                .padding(start = 16.dp),
            text = noticeName
        )
        IconButton(onClick = onClose) {
            Icon(Icons.Filled.Close, contentDescription = "Close")
        }
    }
}

Compose事件与状态简略介绍_第4张图片

国庆七天假期,当到最后一天时给用户一个提示,要上班了,将NationalDayNoticeItem添加到NationalDayVar中,修改NationalDayVar如下

@Composable
fun NationalDayVar(modifier: Modifier = Modifier) {
    Column(modifier = modifier.padding(16.dp)) {
        var day by remember { mutableStateOf(7) }
        if (day<1){
            var showNotice by remember { mutableStateOf(true) }
            if (showNotice){
                NationalDayNoticeItem(
                    onClose = {showNotice = false },
                    noticeName = "你要上班啦"
                )
            }
        }
        Text(
            text = "我们有 $day 天来庆祝国庆节!",
            modifier = modifier.padding(16.dp)
        )
        Row(Modifier.padding(top = 8.dp)) {
            Button(onClick = { day-- }) {
                Text(text = "玩一天")
            }
            Button(onClick = { day = 7 }, Modifier.padding(start = 8.dp)) {
                Text("重新过国庆")
            }
        }

    }
}

我们新增了一个showNotice 的状态当国庆假期只剩一天时,改变状态并弹出提示,为了方便添加了一个重新过国庆的按钮(多希望真的能重新过),效果如下

初始状态是这样的
Compose事件与状态简略介绍_第5张图片
当我们点击玩一天时,day状态会变化,而当day的值为0时showNotice 状态会初始化为true并展示notice
Compose事件与状态简略介绍_第6张图片
这时我们点击X关掉提示,再调休一天,并不会出现提示,这是因为我们记住了showNotice 的值为在点击X时已经变更为false
Compose事件与状态简略介绍_第7张图片
接下来是重头戏,我们点击重新过国庆,这时我们时光倒流,又有七天假期可以庆祝国庆了
Compose事件与状态简略介绍_第8张图片
在我们玩到最后一天时,提示又出现告诉我们要上班了
Compose事件与状态简略介绍_第9张图片
(是不是有一种进入循环或者夏日重现的感觉)按理说showNotice 在我们第一次点击X时已经被置为false并且已经生效,第二次还是会提示我们要上班了,这是因为在day状态变为7时,会有第二次进入day<1这个过程,这时状态showNotice会被重置为true并展示,所以我们可以修改代码如下

@Composable
fun NationalDayVar(modifier: Modifier = Modifier) {
    Column(modifier = modifier.padding(16.dp)) {
        var day by remember { mutableStateOf(7) }
        var showNotice by remember { mutableStateOf(false) }
        var count by remember {mutableStateOf(0)}
        if (day<1){
            if (count==0){
                showNotice = true
            }
            count++
            if (showNotice){
                NationalDayNoticeItem(
                    onClose = {showNotice = false },
                    noticeName = "你要上班啦"
                )
            }
        }
        Text(
            text = "我们有 $day 天来庆祝国庆节!",
            modifier = modifier.padding(16.dp)
        )
        Row(Modifier.padding(top = 8.dp)) {
            Button(onClick = { day-- }) {
                Text(text = "玩一天")
            }
            Button(onClick = { day = 7 }, Modifier.padding(start = 8.dp)) {
                Text("重新过国庆")
            }
        }

    }
}

我们将showNotice状态提前到使用它之前,这时我们再次重新过国庆,就不会弹出提示,谷歌官方将showNotice这个状态的初始化提升到使用它之前这个行为称之为状态提升,这利于我们对状态的管理
Compose事件与状态简略介绍_第10张图片

三.rememberSaveable

正当我玩这个过国庆玩的不亦乐乎时,我不小心手滑了一下,我的手机差点掉到了地上,还好我眼疾手快抓住了它,但是拿起来一看,我的屏幕发生了旋转,本来只有0天过国庆的屏幕,重新变成了七天,我对天发誓我没有按到重新过国庆的按钮。并且我再一次玩到0天时,它又出现了上班提醒,我真的头都大了,我不想看到上班两个字,查阅资料后,发现这是因为在屏幕旋转后我的配置发生了变化,这导致compose退出组合并重组,忘记了所有已经更改的状态。要改变这一情况,就要说到到rememberSaveable 了。

  • rememberSaveable 会自动保存可保存在 Bundle 中的任何值。对于其他值,您可以将其传入自定义 Saver 对象。
  • 不同于.remember,rememberSaveable 可以保存你在上诉情况及其配置更改的情况下(例如屏幕旋转)的状态的值
  • 在重新创建 activity 或进程后,您可以使用 rememberSaveable恢复界面状态。除了在重组后保持状态之外,rememberSaveable 还会在重新创建 activity 和进程之后保留状态。

我们将代码中的 remember更改为rememberSaveable

@Composable
fun NationalDayVar(modifier: Modifier = Modifier) {
    Column(modifier = modifier.padding(16.dp)) {
        var day by rememberSaveable  { mutableStateOf(7) }
        var showNotice by rememberSaveable  { mutableStateOf(false) }
        var count by rememberSaveable  {mutableStateOf(0)}
        ...
       }
}

这样旋转屏幕也不会让我第二次看到上班提醒啦!

题外话

在点击事件的onClick中,需要通过状态去改变函数,而不是直接调用compose函数将可组合项添加进去,参考所有上例;这是因为compose架构旨在用状态管理事件


总结

虽然rememberSaveable可以保存状态更改后的值但这并不是说remember 没有rememberSaveable好,请根据应用的状态和用户体验需求来考虑是使用 remember 还是 rememberSaveable。

你可能感兴趣的:(android,jetpack,android,kotlin)