type
status
date
slug
summary
tags
category
password
icon
21年的时候就简单了解过 Compose,碍于工作被带偏,一直没有机会实践和学习。有幸得到同事提醒,一起参加国内首个免费的 Compose UI 系列 Codelab,比较匆忙,决定学习了解
布局
简单的布局组件
- Row:横向布局
- Column:纵向布局
- Box:层叠布局
约束布局
引用是使用createRefs
( 或createRefFor()
创建的,ConstraintLayout 中的每个可组合项都需要有与之关联的引用。 约束条件是使用constrainAs()
修饰符提供的,该修饰符将引用作为参数,可让您在主体lambda 中指定其约束条件。约束条件是使用linkTo
或其他有用的方法指定的。parent
是一个现有的引用,可用于指定对 ConstraintLayout 可组合项本身的约束条件。
解藕 API 在某些情况下,最好将约束条件与应用它们的布局分离开来,例如我们可能会希望根据屏幕配置来更改约束条件,或在两个约束条件集之间添加动画效果
- 将
ConstraintSet
作为参数传递给ConstraintLayout
- 使用
layoutid
修饰符将在ConstraintSet
中创建的引用分配给可组合项
Intrinsics
Compose 只测量子元素一次,测量两次两次会引发运行时异常,但是,有时在测量子元素之前,我们需要一些有关子元素的信息 Intrinsics 允许在实际测量之前查询子项
- (min|max)IntrinsicWidth:鉴于此宽度,正确绘制内容的最小 / 最大宽度是多少
- (min|max)IntrinsicHeight:鉴于此高度,正确绘制内容的最小 / 最大高度是多少
一些常用的组件
- slots api: 插槽,类似 vue 的 slots,常使用的顶级组件有 Scaffold,常用组件有TopAppBar、 BottomAppBar、 Floating、ActionButton 和 Drawer
- List
- Column 和 Row 也可以实现List的效果
- LazyColumn 和 LazyRow 如果需要指定滑动位置,需要手动设置 scrollState
自定义布局
在 Compose 中,界面元素由可组合函数表示,此类函数在被调用后会发出一部分界面,这部分界面随后会被添加到呈现在屏幕上的界面树中。每个界面元素都有一个父元素,还可能有多个子元素。此外,每个元素在其父元素中都有一个位置,指定为 (x,y) 位置;也都有一个尺寸,指定为 width 和 height。
布局修饰符
您可以使用 layout 修饰符来修改元素的测量和布局方式。Layout 是一个 lambda;它的参数包括您可以测量的元素(以 measurable 的形式传递)以及该可组合项的传入约束条件(以 constraints 的形式传递)。
软键盘
- keyboardOptions:用于启用显示万册 IME 操作
- keyboardActions:当指定 IME 操作被触发后需要执行的行为。
Compose 状态
非结构化状态:
在传统的 Android 开发中,开发者常常是初始化好页面后,每次到达一个状态,就手动将上一个状态修改到新的状态,这种并没有对状态进行一个结构化的管理。可能会出现几个问题:
- 测试:由于 UI 的状态与 Views 代码交织在一起,造成测试困难
- 部分状态的更新:当屏幕有更多的事件时,很容易忘记更新部分状态以响应事件。因此用户可能会看到不一致或不正确的 UI。
- 部分 UI 更新:每次状态更改后手动更新 UI,因此有时候很容易忘记这一点,因此,用户可能看到陈旧的数据。
- 代码复杂性:在这种模式下编码时很难提取一些逻辑,结果代码有变得难以阅读和理解的趋势。
为了解决非结构化状态带来的问题,Android 通过 LiveDate 和 LifeCycle 实现了单项数据流的方式,
单项数据流是一种状态乡下流动而事件向上流动的设计,他的优势有:
- 可测试性:通过将状态与显示他的 UI 分离,可以更轻松地测试 ViewModel 和Activity。
- 状态封装:因为状态只能在一个地方(the viewModel)更新,随着 UI 的增长,你不太可能引入部分状态更新错误。
- UI 一致性:所有的状态更新都痛使用可观察状态持有者立即反应在 UI 中。
状态提升
如果可组合项时无状态的,那它如何才能显示可修改的列表?为实现此目的,我们会使用一种称为状态提升的技术。
Compose 中的状态提升是一种将状态移至可组合项的调用方,以使可组合项无状态的模式。无状态组件更容易测试,往往有更少的错误,并提供更多的重用机会。
上面的 TodoScreen 组件内部是没有状态的,他的数据是外部传入的 items,所谓的状态提升就是自己只负责展示数据,具体数据怎么来怎么变,我不关心,外部传给我什么数据我就按照自己的方式展示。
事实证明,这些参数的组合使得调用方能够从此可组合项中提升状态,为了了解具体的工作原理,我们来探索此可组合项的界面更新循环
- 事件:当用户请求添加或删除时,TodoScreen 会调用 onAddItem 或 onRemoveItem
- 更新状态:TodoScreen 的调用方可以通过更新状态来响应这些事件
- 显示状态:状态更新后,系统将使用新的 items 再次调用 TodoScreen,而且后者可以在界面上显示它们
调用方负责确定保持此状态的位置和方式,不过,他可以合理地存储 items,例如,存储在内存中或从 Room 数据库中读取。TodoScreen 与状态的管理方式时完全解藕的。
应用与可组合项时,这通常意味着向可组合项引入两个参数
- value:T - 要显示的当前值
- onValueChange: (T) → Unit - 请求更改值的事件,其中T是建议的新值。
Compose状态管理
重组
在命令式界面模型中,如果需要更改某个组件,可以在该组件上调用 setter 以更改其内部状态。在 Compose 中,可以使用新数据再次调用可组合函数。这样会导致函数进行重组———系统会根据新数据重新绘制组件。Compose 框架可以智能地仅重组已更改的组件。
remember
将内存引入可组合函数
remember 提供了可组合函数内存
系统会将 remember 计算的值存储在组合树中,而且只有当 remember 的键发生变化时,才会重新计算该值。
可以将 remember 看作是函数为单个对象提供的存储空间,过程与 private val 属性在对象中执行的操作相同

有状态和无状态
使用 remember 存储对象的可组合项会创建内部状态,使该可组合项有状态
在调用方不需要控制状态,并且不必自行管理状态便可使用状态的情况下,“有状态”会非常有用,但是,具有内部状态的可组合项往往不易重复使用,也更难测试。
无状态可组合项是指不保存任何状态的可组合项。实现无状态的一种简单方法是使用状态提升。
MutableState
val (value, setValue) = remember
{
mutableStateOf
("")
}
这个函数通过 remember 给自己添加内存,然后在内存中存储一个 mutableStateOf 创建的MutableStateOf<String> 对象,它提供一个可观察状态容器,是 Compose 内置的类型
对 value 的任何更改动将自动重新组合读取此状态的任何可组合函数。
可以通过以下 MutableState 三种声明一个可组合对象:
val state = remember{ mutableStateOf(default) }
var value by remember { mutableStateOf(default) }
val (value, setValue) = remember { mutableStateOf(default) }
在组合中创建 State<T>(或其他有状态对象)时,务必对其执行 remember 操作,否则每次重组时他将重新初始化。
MutableState<T> 类似于 MutableLiveData<T>,但与 Compose 运行时集成,由于他是可观察的,它会在更新时通知 Compose。

Compose 的状态恢复
rememberSaveable 恢复状态
在重新创建 activity 或进程后,我们可以使用 rememberSaveable 恢复界面状态,rememberSaveable 可以在重组后保持状态,此外,rememberSaveable 也可以重新创建activity 和进程后保持状态。
存储状态的方式
添加到 Bundle 的所有数据类型都会自动保存,如果要保存无法添加到 Bundle 的内容,有以下几种选择:
- Parcelize:最简单的解决方案是对象添加 @Parcelize 注解,对象将变成可序列化状态,并且可以捆绑。
- MapSaver:如果上一个方式不适合,可以使用 mapSaver 定义自己的规则,规定如何将对象转换为系统可以保存到 Bundle 的一组值
- listSaver:未来避免需要为映射定义键,可以使用 listSaver 并将其索引用作键
CompositionLocal
显示传参和隐式传参
通常情况下,在 Compose 中,数据以参数的形式向下流经整个界面树传递给每个可组合函数。但是对于广泛使用的常用数据(如颜色或类型样式),这可能会很麻烦。
未来支持无需将颜色作为显示参数依赖项传递给大多数可组合项,Compose提供了CompositionLocal,可创建以树为作用域的具名对象,这可以用作数据流经界面树的一种隐式方式。
可以通过 MaterialTheme 的 colorScheme,shape 和 typography 属性访问的LocalColorScheme,LocalShapes 和 LocalTypography 属性。

如果需要为 CompositionLocal 提供新值,请使用 CompositionLocalProvider 及其
provides infix
函数。CompositionLocal 的
current
值对应于该组合部分中的某个祖先提供的最接近的值。MaterialTheme
创建 CompositionLocal
- compositionLocalOf
如果更改提供的值,只会使读取其 current 值的组件发生重组。
- staticCompositionLocalOf
与 compositionLocalOf 不同,staticCompositionLocalOf 更改值会导致提供 CompositionLocal 的整个 content lambda 被重组,而不仅仅是在组合中读取 current 值的组件
如果为 CompositionLocal 提供的值发生更改的可能性微乎其微或永远不会更改,使用staticCompositionLocalOf 可提高性能
主题
Material Design 是什么
Material Design 是一个用于创建数字界面的综合设计体系,Material Design
组件(按钮、卡片、开关等)建立在 Material Theming 之上, Material theme
包括颜色、排版和形状属性.
Compose 与 Materail Deign 的关系
如何使用 Material Design
定义主题
使用颜色
处理文本
如果我们需要对一段文本应用多种样式,可以使用 AnnotatedString 类来应用标记,从而为一系列文本添加 SpanStyle。
使用形状
- 作者:shuouyang
- 链接:https://notion-tree.vercel.app/article/443d26ae-e155-4ce0-b78a-5abaadb00bdd
- 声明:本文采用 CC BY-NC-SA 4.0 许可协议,转载请注明出处。