改进以下互动组件的合成性能:
Modifier.clickable
,我们引入了新的 API。借助这些 API
高效的 Indication
实现,例如涟漪效果。
androidx.compose.foundation:foundation:1.7.0+
和
androidx.compose.material:material-ripple:1.7.0+
包含以下 API
更改:
已弃用 |
替换 |
---|---|
|
|
|
Material 库中提供了新的 注意:在本例中,“Material 库”是指 |
|
请执行以下任一操作:
|
本页介绍了行为变更的影响和迁移到 这些新 API
行为变更
以下库版本包含涟漪行为变更:
androidx.compose.material:material:1.7.0+
androidx.compose.material3:material3:1.3.0+
androidx.wear.compose:compose-material:1.4.0+
这些版本的 Material 库不再使用 rememberRipple()
;改为
并使用新的涟漪 API。因此,它们不会查询 LocalRippleTheme
。
因此,如果您在应用中设置了 LocalRippleTheme
,Material
组件将不会使用这些值。
以下部分介绍了如何暂时回退到旧行为
而无需迁移但还是建议您迁移至新 API。对于
请参阅从 rememberRipple
迁移到 ripple
和后续部分。
无需迁移,即可升级 Material 库版本
如需取消屏蔽正在升级的库版本,您可以使用临时
LocalUseFallbackRippleImplementation CompositionLocal
个用于配置的 API
要回退到旧行为的 Material 组件:
CompositionLocalProvider(LocalUseFallbackRippleImplementation provides true) {
MaterialTheme {
App()
}
}
请务必在 MaterialTheme
外部提供此模块,以便旧版涟漪图可以正常呈现
通过 LocalIndication
提供。
以下各部分介绍了如何迁移到新 API。
从 rememberRipple
迁移到 ripple
使用 Material 库
如果您使用的是 Material 库,请直接将 rememberRipple()
替换为
从相应库调用 ripple()
。此 API 会产生涟漪
使用派生自 Material 主题 API 的值。然后,将返回的
对象传递给 Modifier.clickable
和/或其他组件。
例如,以下代码段使用已废弃的 API:
Box(
Modifier.clickable(
onClick = {},
interactionSource = remember { MutableInteractionSource() },
indication = rememberRipple()
)
) {
// ...
}
您应将上面的代码段修改为:
@Composable
private fun RippleExample() {
Box(
Modifier.clickable(
onClick = {},
interactionSource = remember { MutableInteractionSource() },
indication = ripple()
)
) {
// ...
}
}
请注意,ripple()
不再是可组合函数,也不需要
已记住。它还可以在多个组件中重复使用,类似于
因此请考虑将涟漪效果提取为顶级值,
保存分配。
实现自定义设计体系
如果您要实现自己的设计系统,并且之前使用
rememberRipple()
,以及用于配置涟漪效果的自定义 RippleTheme
,
您应改为提供自己的涟漪 API 来委托给涟漪节点
material-ripple
中公开的 API。然后,您的组件可以使用您自己的涟漪图
直接使用主题值有关详情,请参阅迁移
RippleTheme
起。
从 RippleTheme
迁移
暂时停用行为变更
Material 库有一个临时的 CompositionLocal
,
LocalUseFallbackRippleImplementation
,您可以使用它来配置
要回退到使用 rememberRipple
的 Material 组件。这样,
rememberRipple
继续查询 LocalRippleTheme
。
以下代码段演示了如何使用 LocalUseFallbackRippleImplementation CompositionLocal
API:
CompositionLocalProvider(LocalUseFallbackRippleImplementation provides true) {
MaterialTheme {
App()
}
}
如果您使用的是基于 Material 构建的自定义应用主题,您可以 安全地将本地组合作为应用主题的一部分提供:
@OptIn(ExperimentalMaterialApi::class)
@Composable
fun MyAppTheme(content: @Composable () -> Unit) {
CompositionLocalProvider(LocalUseFallbackRippleImplementation provides true) {
MaterialTheme(content = content)
}
}
有关更多信息,请参阅在不影响 迁移部分。
使用 RippleTheme
停用给定组件的涟漪效果
material
和 material3
库公开了 RippleConfiguration
和
LocalRippleConfiguration
,可让您配置
子树中的涟漪效果。请注意,RippleConfiguration
和
LocalRippleConfiguration
处于实验阶段,仅适用于每个组件
自定义。以下模式不支持全局/主题范围的自定义
API;请参阅使用 RippleTheme
全局更改
应用,详细了解该应用场景。
例如,以下代码段使用已废弃的 API:
private object DisabledRippleTheme : RippleTheme {
@Composable
override fun defaultColor(): Color = Color.Transparent
@Composable
override fun rippleAlpha(): RippleAlpha = RippleAlpha(0f, 0f, 0f, 0f)
}
// ...
CompositionLocalProvider(LocalRippleTheme provides DisabledRippleTheme) {
Button {
// ...
}
}
您应将上面的代码段修改为:
CompositionLocalProvider(LocalRippleConfiguration provides null) {
Button {
// ...
}
}
使用 RippleTheme
更改给定组件的涟漪的颜色/alpha 值
如上一部分中所述,RippleConfiguration
和
LocalRippleConfiguration
是实验性 API,仅适用于
每个组件的自定义。
例如,以下代码段使用已废弃的 API:
private object DisabledRippleThemeColorAndAlpha : RippleTheme {
@Composable
override fun defaultColor(): Color = Color.Red
@Composable
override fun rippleAlpha(): RippleAlpha = MyRippleAlpha
}
// ...
CompositionLocalProvider(LocalRippleTheme provides DisabledRippleThemeColorAndAlpha) {
Button {
// ...
}
}
您应将上面的代码段修改为:
@OptIn(ExperimentalMaterialApi::class)
private val MyRippleConfiguration =
RippleConfiguration(color = Color.Red, rippleAlpha = MyRippleAlpha)
// ...
CompositionLocalProvider(LocalRippleConfiguration provides MyRippleConfiguration) {
Button {
// ...
}
}
使用 RippleTheme
全局更改应用中的所有涟漪
以前,您可以使用 LocalRippleTheme
定义
主题级别。这本质上是自定义
系统对局部和涟漪的影响。不要公开一个泛型
主题基元,material-ripple
现在公开了 createRippleModifierNode()
函数。此函数允许设计系统库创建
订购 wrapper
实现,这些实现会查询其主题值,然后委托
此函数创建的节点上的涟漪实现。
这样,设计系统就可以直接查询所需的内容,
需要使用用户可配置的主题层,而无需遵循
material-ripple
层提供的内容。这一变化也使得
明确涟漪遵循什么主题/规范,因为它是
定义该协定的 Ripple API 本身,
由主题衍生而来
如需指导,请参阅 Material 中的 Ripple API 实现 库,并根据需要替换对 Material CompositionLocal 的调用, 自己的设计体系。
从 Indication
迁移到 IndicationNodeFactory
经过Indication
周边
如果您只是创建一个要传递的 Indication
,例如创建一个
传递给 Modifier.clickable
或 Modifier.indication
时,
进行任何更改IndicationNodeFactory
继承自 Indication
、
确保一切都能继续编译和运行
正在创建“Indication
”
如果您要创建自己的 Indication
实现,迁移操作应
非常简单。例如,假设一个 Indication
应用了
按下时的缩放效果:
object ScaleIndication : Indication {
@Composable
override fun rememberUpdatedInstance(interactionSource: InteractionSource): IndicationInstance {
// key the remember against interactionSource, so if it changes we create a new instance
val instance = remember(interactionSource) { ScaleIndicationInstance() }
LaunchedEffect(interactionSource) {
interactionSource.interactions.collectLatest { interaction ->
when (interaction) {
is PressInteraction.Press -> instance.animateToPressed(interaction.pressPosition)
is PressInteraction.Release -> instance.animateToResting()
is PressInteraction.Cancel -> instance.animateToResting()
}
}
}
return instance
}
}
private class ScaleIndicationInstance : IndicationInstance {
var currentPressPosition: Offset = Offset.Zero
val animatedScalePercent = Animatable(1f)
suspend fun animateToPressed(pressPosition: Offset) {
currentPressPosition = pressPosition
animatedScalePercent.animateTo(0.9f, spring())
}
suspend fun animateToResting() {
animatedScalePercent.animateTo(1f, spring())
}
override fun ContentDrawScope.drawIndication() {
scale(
scale = animatedScalePercent.value,
pivot = currentPressPosition
) {
this@drawIndication.drawContent()
}
}
}
您可以分两个步骤进行迁移:
将
ScaleIndicationInstance
迁移为DrawModifierNode
。API Surface 与IndicationInstance
非常相似:它会公开一个DrawModifierNode
ContentDrawScope#draw()
函数,该函数等同于IndicationInstance#drawContent()
。您需要更改该函数 直接在节点内实现collectLatest
逻辑,而不是使用Indication
。例如,以下代码段使用已废弃的 API:
private class ScaleIndicationInstance : IndicationInstance {
var currentPressPosition: Offset = Offset.Zero
val animatedScalePercent = Animatable(1f)
suspend fun animateToPressed(pressPosition: Offset) {
currentPressPosition = pressPosition
animatedScalePercent.animateTo(0.9f, spring())
}
suspend fun animateToResting() {
animatedScalePercent.animateTo(1f, spring())
}
override fun ContentDrawScope.drawIndication() {
scale(
scale = animatedScalePercent.value,
pivot = currentPressPosition
) {
this@drawIndication.drawContent()
}
}
}您应将上面的代码段修改为:
private class ScaleIndicationNode(
private val interactionSource: InteractionSource
) : Modifier.Node(), DrawModifierNode {
var currentPressPosition: Offset = Offset.Zero
val animatedScalePercent = Animatable(1f)
private suspend fun animateToPressed(pressPosition: Offset) {
currentPressPosition = pressPosition
animatedScalePercent.animateTo(0.9f, spring())
}
private suspend fun animateToResting() {
animatedScalePercent.animateTo(1f, spring())
}
override fun onAttach() {
coroutineScope.launch {
interactionSource.interactions.collectLatest { interaction ->
when (interaction) {
is PressInteraction.Press -> animateToPressed(interaction.pressPosition)
is PressInteraction.Release -> animateToResting()
is PressInteraction.Cancel -> animateToResting()
}
}
}
}
override fun ContentDrawScope.draw() {
scale(
scale = animatedScalePercent.value,
pivot = currentPressPosition
) {
this@draw.drawContent()
}
}
}迁移
ScaleIndication
以实现IndicationNodeFactory
。由于 现在将收集逻辑移到了节点中,这是一个非常简单的工厂, 对象的唯一职责是创建节点实例。例如,以下代码段使用已废弃的 API:
object ScaleIndication : Indication {
@Composable
override fun rememberUpdatedInstance(interactionSource: InteractionSource): IndicationInstance {
// key the remember against interactionSource, so if it changes we create a new instance
val instance = remember(interactionSource) { ScaleIndicationInstance() }
LaunchedEffect(interactionSource) {
interactionSource.interactions.collectLatest { interaction ->
when (interaction) {
is PressInteraction.Press -> instance.animateToPressed(interaction.pressPosition)
is PressInteraction.Release -> instance.animateToResting()
is PressInteraction.Cancel -> instance.animateToResting()
}
}
}
return instance
}
}您应将上面的代码段修改为:
object ScaleIndicationNodeFactory : IndicationNodeFactory {
override fun create(interactionSource: InteractionSource): DelegatableNode {
return ScaleIndicationNode(interactionSource)
}
override fun hashCode(): Int = -1
override fun equals(other: Any?) = other === this
}
使用 Indication
创建 IndicationInstance
在大多数情况下,应使用 Modifier.indication
为Indication
组件。不过,在极少数情况下
IndicationInstance
正在使用rememberUpdatedInstance
,您需要更新
实现来检查 Indication
是否为 IndicationNodeFactory
,以便您
可以使用更轻量的实现例如,Modifier.indication
将
如果创建的节点为 IndicationNodeFactory
,则会在内部将其委派给该节点。如果
否则,它将使用 Modifier.composed
来调用 rememberUpdatedInstance
。