Jetpack Compose中的state核心思想

Compose 中的状态

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

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

  • 聊天应用中最新收到的消息。
  • 用户的个人资料照片。
  • 在项列表中的滚动位置。

关键提示:状态决定界面在任何特定时间的显示内容。

下面的示例通过构建一个健康App应用来展示如何正确的使用Compose中的状态。

构建的第一项功能是饮水计数器,用于记录您一天饮用了多少杯水。

创建一个名为 WaterCounter 的可组合函数,其中包含一个 Text 可组合项,用于显示饮水杯数。饮水杯数应存储在名为 count 的值中。

创建一个包含 WaterCounter 可组合函数的新文件 WaterCounter.kt,如下所示:

import androidx.compose.foundation.layout.padding
import androidx.compose.material.Text
import androidx.compose.runtime.Composable
import androidx.compose.ui.Modifier
import androidx.compose.ui.unit.dp

@Composable
fun WaterCounter(modifier: Modifier = Modifier) {
   
   Column(modifier = modifier.padding(16.dp)) {
   	
	   val count = 0
	   Text(
	       text = "You've had $count glasses.",
	       modifier = modifier.padding(16.dp)
	   )
	   Button(onClick = {
    count++ }, Modifier.padding(top = 8.dp)) {
   
	       Text("Add one")
	   }
   }
}

创建一个代表主屏幕的文件 WellnessScreen.kt,然后调用 WaterCounter 函数:

import androidx.compose.runtime.Composable
import androidx.compose.ui.Modifier

@Composable
fun WellnessScreen(modifier: Modifier = Modifier) {
   
   WaterCounter(modifier)
}

打开 MainActivity.kt,在 activity 的 setContent 块内调用新创建的 WellnessScreen 可组合项,如下所示:

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

提示:setContent 内部的 MainActivity 中使用的应用主题取决于项目名称。此处假定项目名为 BasicStateCodelab。

如果现在运行应用,您会看到我们的基本饮水计数器屏幕,其中包含硬编码的饮水杯数。

WaterCounter 可组合函数的状态为变量 count。但是,点击按钮时,您会发现没有任何反应。为 count 变量设置不同的值不会使 Compose 将其检测为状态更改,因此不会产生任何效果。这是因为,当状态发生变化时,您尚未指示 Compose 应重新绘制屏幕(即“重组”可组合函数)。

可组合函数中的记忆功能

Compose 应用通过调用可组合函数将数据转换为界面。组合是指 Compose 在执行可组合项时构建的界面描述。如果发生状态更改,Compose 会使用新状态重新执行受影响的可组合函数,从而创建更新后的界面。这一过程称为重组。Compose 还会查看各个可组合项需要哪些数据,以便仅重组数据发生了变化的组件,而避免重组未受影响的组件。

  • 组合:Jetpack Compose 在执行可组合项时构建的界面描述。
  • 初始组合:通过首次运行可组合项创建组合。
  • 重组:在数据发生变化时重新运行可组合项以更新组合。

为此,Compose 需要知道要跟踪的状态,以便在收到更新时安排重组。

Compose 采用特殊的状态跟踪系统,可以为读取特定状态的任何可组合项安排重组。 这让Compose 能够实现精细控制,并且仅重组需要更改的可组合函数,而不是重组整个界面。这将通过同时跟踪针对状态的“写入”(即状态变化)和针对状态的“读取”来实现。

使用 Compose 的 StateMutableState 类型让 Compose 能够观察到状态。

Compose 会跟踪每个读取状态 value 属性的可组合项,并在其 value 更改时触发重组。您可以使用 mutableStateOf 函数来创建可观察的 MutableState。它接受初始值作为封装在 State 对象中的参数,这样便可使其 value 变为可观察。

现在修改代码如下:

import androidx.compose.runtime.MutableState
import androidx.compose.runtime.mutableStateOf

@Composable
fun WaterCounter(modifier: Modifier = Modifier) {
   
   Column(modifier = modifier.padding(16.dp)) {
   
      // Changes to count are now tracked by Compose
       val count: MutableState<Int> = mutableStateOf(0)

       Text("You've had ${
     count.value} glasses.")
        Button(onClick = {
    count.value++ }, Modifier.padding(top = 8.dp)) {
   
           Text("Add one")
       }
   }
}

如前所述,对 count 所做的任何更改都会安排对自动重组读取 count 的 value 的所有可组合函数进行重组。在此情况下,点击按钮即会触发重组 WaterCounter。

如果现在运行应用,您会再次发现没有发生任何变化!

安排重组的过程没有问题。不过,当重组发生时,变量 count 会重新初始化为 0,因此我们需要通过某种方式在重组后保留此值。

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

现在继续修改代码如下:

import androidx.compose.runtime.remember

@Composable
fun WaterCounter(modifier: Modifier = Modifier) {
   
    Column(modifier = modifier.padding(16.dp)) {
   
        val count: MutableState<Int> = remember {
    mutableStateOf(0) }
        Text("You've had ${
     count.value} glasses.")
        Button(onClick = {
    count.value++ }, Modifier.padding(top = 8.dp)) {
   
            Text("Add one")
        }
    }
}

remember 和 mutableStateOf 通常在可组合函数中一起使用。您可以将 remember 视为一种在组合中存储单个对象的机制,就像私有 val 属性在对象中执行的操作一样。

这里还可以使用 Kotlin 的委托属性来简化 count 的使用,通过关键字 by 将 count 定义为 var。通过添加委托的 getter 和 setter 导入内容,我们可以间接读取 count 并将其设置为可变,而无需每次都显式引用 MutableState 的 value 属性。

修改代码如下:

import androidx.compose.runtime.getValue
import androidx.compose.runtime.setValue

@Composable
fun WaterCounter(modifier: Modifier = Modifier) {
   
   Column(modifier = modifier.padding(16.dp)) {
   
       var count by remember {
    mutableStateOf(0) }

       Text("You've had $count glasses.")
       Button(onClick = {
    count++ }, Modifier.padding(top = 8.dp)) {
   
           Text("Add one")
       }
   }
}

现在运行应用,您的计数器已就绪且可正常运行!

这种安排可与用户形成数据流反馈循环:

  • 界面向用户显示状态(当前计数显示为文本)。
  • 用户生成的事件会与现有状态合并以生成新状态(点击按钮会为当前计数加一)

状态驱动型界面

Compose 是一个声明性界面框架。它描述界面在特定状况下的状态,而不是在状态发生变化时移除界面组件或更改其可见性。调用重组并更新界面后,可组合项最终可能会进入或退出组合。

此方法可避免像针对视图系统那样手动更新视图的复杂性。这也不太容易出错,因为您不会忘记根据新状态更新视图,因为系统会自动执行此过程。

如果在初始组合期间或重组期间调用了可组合函数,则认为其存在于组合中。未调用的可组合函数(例如,由于该函数在 if 语句内调用且未满足条件)不存在于组合中。

关键提示:如果界面是相对用户而言的,那么界面状态就是相对应用而言的。这就像同一枚硬币的两面,界面是界面状态的直观呈现。对界面状态所做的任何更改都会立即反映在界面中。

组合的输出是描述界面的树结构。

Android Studio 的布局检查器工具可用于检查 Compose 生成的应用布局。我们接下来将执行此操作。

为了演示此过程,请修改代码,以根据状态显示界面。打开 WaterCounter,如果 count 大于 0,则显示 Text:

@Composable
fun WaterCounter(modifier: Modifier = Modifier) {
   
   Column(modifier = modifier.padding(16.dp)) {
   
       var count by remember {
    mutableStateOf(0) }

       if (count > 0) {
   
           // This text is present if the button has been clicked
           // at least once; absent otherwise
           Text("You've had $count glasses.")
       }
       Button(onClick = {
    count++ }, Modifier.padding(top = 
评论 3
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

川峰

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值