Jetpack Compose 2025 年 8 月版本的新功能

近期Jetpack Compose 2025 年 8 月版本 已正式发布. 本次发布包含 Compose 1.9 版本的核心模块(详见完整的BOM), 引入了用于渲染阴影, 2D 滚动, 文本变换相关的富样式, 改进的列表性能等新 API!
要想使用本次发布的版本的话, 你可以将自己项目的Compose BOM版本升级至2025.08.00
:
less
implementation(platform("androidx.compose:compose-bom:2025.08.00"))
阴影
很高兴 Google 官方推出两个备受期待的Modifier: Modifier.dropShadow()
和 Modifier.innerShadow()
, 允许你渲染和阴影效果(与现有的 Modifier.shadow()
不同, 后者基于照明模型且渲染基于高度的阴影).
Modifier.dropShadow()
Modifier.dropShadow()
在内容后面绘制阴影. 你可以将其添加到@Composable
链中, 并指定半径, 颜色和扩散范围. 请注意, 应显示在阴影上方的内容(如背景)应在 Modifier.dropShadow()
之后渲染.
less
@Composable
@Preview(showBackground = true)
fun SimpleDropShadowUsage() {
val pinkColor = Color(0xFFe91e63)
val purpleColor = Color(0xFF9c27b0)
Box(Modifier.fillMaxSize()) {
Box(
Modifier
.size(200.dp)
.align(Alignment.Center)
.dropShadow(
RoundedCornerShape(20.dp),
dropShadow = DropShadow(
15.dp,
color = pinkColor,
spread = 10.dp,
alpha = 0.5f
)
)
.background(
purpleColor,
shape = RoundedCornerShape(20.dp)
)
)
}
}

图1. Shape
周围绘制的阴影
Modifier.innerShadow()
Modifier.innerShadow()
在提供的Shape
的内侧绘制阴影:
less
@Composable
@Preview(showBackground = true)
fun SimpleInnerShadowUsage() {
val pinkColor = Color(0xFFe91e63)
val purpleColor = Color(0xFF9c27b0)
Box(Modifier.fillMaxSize()) {
Box(
Modifier
.size(200.dp)
.align(Alignment.Center)
.background(
purpleColor,
shape = RoundedCornerShape(20.dp)
)
.innerShadow(
RoundedCornerShape(20.dp),
innerShadow = InnerShadow(
15.dp,
color = Color.Black,
spread = 10.dp,
alpha = 0.5f
)
)
)
}
}

图2. 应用于Shape
的 Modifier.innerShadow()
内阴影的顺序非常重要. 内阴影绘制在内容之上, 因此在上面的示例中, Google 官方需要将Modifier.innerShadow()
在Modifier.background()
之后. 当将其应用于Image
等元素时, Google 官方需要采取类似的操作. 在此示例中, Google 官方放置了一个单独的 Box 来在Image
上方渲染阴影:
less
@Composable
@Preview(showBackground = true)
fun PhotoInnerShadowExample() {
Box(Modifier.fillMaxSize()) {
val shape = RoundedCornerShape(20.dp)
Box(
Modifier
.size(200.dp)
.align(Alignment.Center)
) {
Image(
painter = painterResource(id = R.drawable.cape_town),
contentDescription = "Image with Inner Shadow",
contentScale = ContentScale.Crop,
modifier = Modifier.fillMaxSize()
.clip(shape)
)
Box(
modifier = Modifier.fillMaxSize()
.innerShadow(
shape,
innerShadow = InnerShadow(15.dp,
spread = 15.dp)
)
)
}
}
}

图3. Image
上的内阴影
新增可见性相关的 Modifier
Compose UI 1.8 引入了 onLayoutRectChanged
, 这是一种高效的追踪屏幕上元素位置的新方式. Google 官方基于此 API 引入 onVisibilityChanged
和 onFirstVisible
以支持常见用例. 这些 API 接受可选参数, 用于指定在触发操作前元素可见的最小比例或时间长度.
使用 onVisibilityChanged
处理基于可见性触发的 UI 变化或副作用, 例如自动播放/暂停视频或启动动画:
scss
LazyColumn {
items(feedData) { video ->
VideoRow(
video,
Modifier.onVisibilityChanged(minDurationMs = 500, minFractionVisible = 1f) {
visible ->
if (visible) video.play() else video.pause()
},
)
}
}
使用 onFirstVisible
处理元素首次在屏幕上可见时的场景, 例如记录展示次数:
scss
LazyColumn {
items(100) {
Box(
Modifier
// Log impressions when item has been visible for 500ms
.onFirstVisible(minDurationMs = 500) { /* log impression */ }
.clip(RoundedCornerShape(16.dp))
.drawBehind { drawRect(backgroundColor) }
.fillMaxWidth()
.height(100.dp)
)
}
}
OutputTransformation
富样式支持
BasicTextField
现在支持在 OutputTransformation
内部添加样式, 如颜色和字体粗细.
新的 TextFieldBuffer.addStyle()
方法允许你应用 SpanStyle
或 ParagraphStyle
来更改文本的外观, 而不会更改底层的 TextFieldState
. 这对于视觉上格式化输入(如电话号码或信用卡号码)非常有用. 此方法只能在 OutputTransformation
内调用.
scss
// Format a phone number and color the punctuation
val phoneTransformation = OutputTransformation {
// 1234567890 -> (123) 456-7890
if (length == 10) {
insert(0, "(")
insert(4, ") ")
insert(9, "-")
// Color the added punctuation
val gray = Color(0xFF666666)
addStyle(SpanStyle(color = gray), 0, 1)
addStyle(SpanStyle(color = gray), 4, 5)
addStyle(SpanStyle(color = gray), 9, 10)
}
}
BasicTextField(
state = myTextFieldState,
outputTransformation = phoneTransformation
)
LazyLayout
LazyLayout
的所有构建块现已稳定! 查看 LazyLayoutMeasurePolicy
, LazyLayoutItemProvider
和 LazyLayoutPrefetchState
以构建你自己的 Lazy 组件.
预加载改进
随着新预加载行为的引入, Lazy List 和 Lazy Grid 的滚动性能有了显著提升. 你现在可以定义一个 LazyLayoutCacheWindow 来预加载更多内容. 默认情况下, 仅在滚动方向上提前渲染一个项目, 且当项目滚出屏幕后会被丢弃. 你现在可以通过viewport
大小或dp大小的分数来自定义预加载的前向项目数量和保留的后向项目数量. 当你选择使用LazyLayoutCacheWindow
时, 项目会立即开始在前向区域进行预加载.
此配置的入口点在LazyListState
中, 它接受缓存窗口大小:
kotlin
@OptIn(ExperimentalFoundationApi::class)
@Composable
private fun LazyColumnCacheWindowDemo() {
// Prefetch items 150.dp ahead and retain items 100.dp behind the visible viewport
val dpCacheWindow = LazyLayoutCacheWindow(ahead = 150.dp, behind = 100.dp)
// Alternatively, prefetch/retain items as a fraction of the list size
// val fractionCacheWindow = LazyLayoutCacheWindow(aheadFraction = 1f, behindFraction = 0.5f)
val state = rememberLazyListState(cacheWindow = dpCacheWindow)
LazyColumn(state = state) {
items(1000) { Text(text = "$it", fontSize = 80.sp) }
}
}

注意: 预加载会渲染比当前可见更多项------新缓存窗口 API 可能增加预加载量. 这意味着项的
LaunchedEffect
和DisposableEffect
可能更早执行------请勿将其作为可见性信号(例如用于展示栈迹). 相反, Google 官方建议使用新的onFirstVisible
和onVisibilityChanged
API. 即使你目前未手动自定义LazyLayoutCacheWindow
, 也应避免将@Composable
效果作为内容可见性的信号, 因为此新预加载机制将在未来版本中默认启用.
滚动
2D 滚动 API
在发布 Draggable2D
之后, Scrollable2D
现已推出, 为 Compose 带来了二维滚动功能. 虽然现有的 Scrollable
Modifier支持单向滚动, 但 Scrollable2D
同时支持二维滚动和滑动操作. 这使你能够创建在所有方向上移动的更复杂布局, 例如电子表格或图片查看器. 嵌套滚动也得到支持, 以适应 2D 场景.
ini
val offset = remember { mutableStateOf(Offset.Zero) }
Box(
Modifier.size(150.dp)
.scrollable2D(
state =
rememberScrollable2DState { delta ->
offset.value = offset.value + delta // update the state
delta // indicate that we consumed all the pixels available
}
)
.background(Color.LightGray),
contentAlignment = Alignment.Center,
) {
Text(
"X=${offset.value.x.roundToInt()} Y=${offset.value.y.roundToInt()}",
style = TextStyle(fontSize = 32.sp),
)
}

滚动互操作性改进
为提升与 View
的滚动和嵌套滚动互操作性, 本次更新包含以下 bug 修复和新功能:
- 现可使用
ViewTreeObserver#onScrollChangeListener
监听 Compose 滚动事件. - 修复了 Compose 和
View
之间在滑动动画过程中错误的速度分发的问题. - Compose 现在会以正确顺序调用视图的嵌套滚动回调.
- 在
AndroidView
中嵌套NestedScrollView
时, 嵌套滚动功能得到正确支持.
通过在堆栈栈迹中添加源信息改进crash分析
Google 官方了解到, 当你的代码未出现在堆栈栈迹中时, 调试 Compose crash 可能较为困难. 为解决此问题, Google 官方提供了一个新的可选 API, 用于提供更丰富的crash 位置详细信息, 包括@Composable
的名称和位置, 使你能够:
- 高效地识别并解决crash源.
- 更轻松地隔离crash以生成可重复的示例.
- 调查之前仅显示内部堆栈帧的crash.
请注意, Google 官方不建议在发布构建中使用此 API, 因为收集这些额外信息会影响性能, 且它在压缩 APK 中也不起作用.
要启用此功能, 请在应用程序入口点添加以下代码行. 理想情况下, 应在创建任何@Composable
之前进行此配置, 以确保堆栈栈迹信息被收集:
kotlin
class App : Application() {
override fun onCreate() {
// Enable only for debug flavor to avoid perf regressions in release
Composer.setDiagnosticStackTraceEnabled(BuildConfig.DEBUG)
}
}
新注解和 Lint 检查
Google 官方引入了一个新的运行时注解库, 该库暴露了编译器和工具(如 Lint 检查)使用的注解. 这使得非 Compose 模块可以使用这些注解, 而无需依赖 Compose 运行时库. @Stable
, @Immutable
和 @StableMarker
注解已移至 runtime-annotation
, 允许你对不依赖 Compose 的类和函数进行注解.
此外, Google 官方新增了两个注解及对应的代码检查:
@RememberInComposition
: 用于标记构造函数, 函数和属性获取器的注解, 指示这些元素不得在@Composable
内部直接调用, 否则将引发代码检查错误.@FrequentlyChangingValue
: 一个用于标记函数和属性获取器的注解, 用于指示它们不应在@Composable
内部直接调用, 因为这可能导致频繁的重新组合(例如, 标记滚动位置值和动画值). 相应的 lint 检查将提供警告.
附加更新
- 为简化兼容性并提升 lint 检查支持的稳定性, Compose 现要求使用 Android Gradle Plugin(AGP)和Lint 的 8.8.2 或更高版本. 请查阅新文档以获取更多信息.
- 添加了两个用于
ContextMenu
的新 API: Modifier.appendTextContextMenuComponents()
: 向ContextMenu
添加新项.Modifier.filterTextContextMenuComponents()
: 从ContextMenu
中移除项.
好吧, 今天的内容就分享到这里吧!
一家之言, 欢迎拍砖!
Happy Coding! Stay GOLDEN!