JetPack Compose 基础(4) state

状态以及 Jetpack Compose 如何使用和操作状态。

在我们深入研究之前,定义状态到底是什么很有用。 从本质上讲,应用程序中的状态是任何可以随时间变化的值。 这是一个非常广泛的定义,包括从 Room 数据库到类上的变量的所有内容。

目标:

什么是单向数据流
如何在 UI 中考虑状态和事件
如何在 Compose 中使用 Architecture Component 的 ViewModel 和 LiveData 来管理状态
Compose 如何使用状态来绘制屏幕
何时将状态移动到调用者
如何在 Compose 中使用内部状态
如何使用 State<T> 将状态与 Compose 集成

state_ui.png

使用单向数据流
为了帮助解决非结构化状态的这些问题,我们引入了包含 ViewModel 和 LiveData 的 Android 架构组件。

ViewModel 允许您从 UI 中提取状态并定义 UI 可以调用以更新该状态的事件

官方的例子理解原理:

ViewModel 还公开了一个事件:onNameChanged。 此事件由 UI 调用以响应用户事件,例如每当 EditText 的文本更改时此处会发生什么。

回到我们之前讨论过的 UI 更新循环,我们可以看到这个 ViewModel 如何与事件和状态结合在一起。

事件 – onNameChanged 在文本输入更改时由 UI 调用
更新状态 - onNameChanged 进行处理,然后设置 _name 的状态
显示状态 - 调用名称的观察者,通知 UI 状态变化
通过以这种方式构建我们的代码,我们可以认为事件“向上”流向 ViewModel。 然后,为了响应事件,ViewModel 将进行一些处理并可能更新状态。 当状态更新时,它会“向下”流向
viemodel_state_event_activity.png

这种模式称为单向数据流。 单向数据流是一种状态向下流动而事件向上流动的设计。 通过以这种方式构建我们的代码,我们获得了一些优势:

  • 可测试性——通过将状态与显示它的 UI 分离,可以更轻松地测试 ViewModel 和 Activity
  • 状态封装——因为状态只能在一个地方(ViewModel)更新,随着 UI 的增长,你不太可能引入部分状态更新错误
  • UI 一致性——所有状态更新都通过使用可观察状态持有者立即反映在 UI 中

因此,虽然这种方法确实添加了更多代码,但使用单向数据流处理复杂的状态和事件往往更容易、更可靠。

单向数据流是一种事件向上流动而状态向下流动的设计。
例如,在 ViewModel 中,事件通过来自 UI 的方法调用传递,而状态使用 LiveData 向下流动。
它不仅仅是描述 ViewModel 的术语——任何事件向上流动和状态下降的设计都是单向的。

如何使用 ViewModel 在 Compose 中使用单向数据流。

上面理论,使用 ViewModel 和 LiveData 探索了 Android View 系统中的单向数据流。

StateCodeLab 项目 解读

TodoScreen.kt – 这些可组合项直接与状态交互,我们将在探索 compose 状态时编辑此文件。
TodoComponents.kt – 这些可组合定义了我们将用于构建 TodoScreen 的可重用 UI 位。 您无需编辑这些可组合项即可完成此 Codelab。
这种文件划分有点随意,将 TodoScreen.kt 中的代码集中在状态上。 在实践中,这些组合项可能位于同一个文件中,或者分布在多个文件中,具体取决于您在项目中如何使用它们。

  1. TodoScreen 函数

这个可组合显示一个可编辑的 TODO 列表,但它没有任何自己的状态。 请记住,状态是任何可以更改的值——但 TodoScreen 的任何参数都不能修改。

items – 要显示在屏幕上的不可变项目列表
onAddItem – 用户请求添加项目时的事件
onRemoveItem – 用户请求删除项目时的事件

事实上,这个可组合是无状态的。 它只显示传入的项目列表,无法直接编辑列表。 相反,它传递了两个可以请求更改的事件 onRemoveItem 和 onAddItem。

这就提出了一个问题:如果它是无状态的,它如何显示可编辑列表? 它通过使用一种称为状态提升的技术来做到这一点。 状态提升是向上移动状态以使组件无状态的模式。 无状态组件更容易测试,往往有更少的错误,并提供更多的重用机会。

事件 – 当用户请求添加或删除项目时 TodoScreen 调用 onAddItem 或 onRemoveItem
更新状态——TodoScreen 的调用者可以通过更新状态来响应这些事件
显示状态 - 当状态更新时,TodoScreen 将使用新项目再次调用,并且可以在屏幕上显示它们

调用者负责确定在何处以及如何保持此状态。 它可以存储项目但有意义,例如在内存中或从 Room 数据库中读取它们。 TodoScreen 与状态的管理方式完全分离。

使用这个 ViewModel 从 TodoScreen 提升状态。 完成后,我们将创建一个单向数据流设计

viemodel-todoscreen.png

TodoScreen 集成到 TodoActivity.kt

class TodoActivity : AppCompatActivity() {

    val todoViewModel by viewModels<TodoViewModel>()

    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)
        setContent {
            StateCodelabTheme {
                Surface {
                    TodoActivityScreen(todoViewModel)
                }
            }
        }
    }
}

@Composable
private fun TodoActivityScreen(todoViewModel: TodoViewModel) {
    TodoScreen(
        items = todoViewModel.todoItems,
        currentlyEditing = todoViewModel.currentEditItem,
        onAddItem = todoViewModel::addItem,
        onRemoveItem = todoViewModel::removeItem,
        onStartEdit = todoViewModel::onEditItemSelected,
        onEditItemChange = todoViewModel::onEditItemChange,
        onEditDone = todoViewModel::onEditDone
    )
}

Android Studio 在启动新 Compose 项目时创建的默认主题。
Surface 为应用程序添加背景,并配置文本颜色。

Flow the events up 事件向上流动

Kotlin 提示
您还可以使用方法引用语法生成一个调用单个方法的 lambda。 这将从方法调用中创建一个 lambda。 使用方法引用语法,上面的 onAddItem 也可以表示为 onAddItem = todoViewModel::addItem。

Pass the state down 向下传递状态

现在我们已经探索了如何使用 compose 和 ViewModels 来构建单向数据流,让我们来探索 compose 如何在内部与状态交互。

有状态的可组合是一种可以随时间改变的状态组合。

重新组合是再次运行相同的组合以在其数据发生变化时更新树的过程

Compose 生成一棵树,但它与您可能熟悉的 Android 视图系统中的 UI 树有点不同。 compose 生成了一棵可组合的树,而不是一棵 UI 小部件树。

我们不希望每次 重新组合时都会改变。 为此,我们需要一个地方来记住我们在上一个构图中使用的元素。 Compose 允许我们将值存储在组合树中,因此我们可以更新相应的操作 以将【值或者状态】 存储在组合树中。

每次 重构时一些元素都会更新的原因是 有一个隐藏的副作用。 副作用是在可组合函数的执行之外可见的任何更改。

Remember

remember 给出了一个可组合的函数内存。

由记住计算的值将存储在组合树中,并且只有在要记住的键发生变化时才会重新计算。

您可以将 memory 视为将单个对象的存储空间分配给函数,就像私有 val 属性在对象中所做的那样。

可组合函数可以使用 remember 可组合项记住单个对象。系统会在初始组合期间将由 remember 计算的值存储在组合中,并在重组期间返回存储的值。remember 既可用于存储可变对象,又可用于存储不可变对象。

最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
禁止转载,如需转载请通过简信或评论联系作者。
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 206,839评论 6 482
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 88,543评论 2 382
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 153,116评论 0 344
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 55,371评论 1 279
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 64,384评论 5 374
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 49,111评论 1 285
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 38,416评论 3 400
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 37,053评论 0 259
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 43,558评论 1 300
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 36,007评论 2 325
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 38,117评论 1 334
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 33,756评论 4 324
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 39,324评论 3 307
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 30,315评论 0 19
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 31,539评论 1 262
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 45,578评论 2 355
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 42,877评论 2 345

推荐阅读更多精彩内容