為提升使用 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 Design 程式庫
如果您使用 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 遷移
使用 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 層提供的內容。這項變更也更明確地指出波紋符合的主題/規格,因為定義合約的是波紋 API 本身,而非從主題隱含衍生。
如需指引,請參閱 Material 程式庫中的水波紋 API 實作,並視需要為自己的設計系統取代對 Material 組合本機值的呼叫。
從 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。DrawModifierNode的 API 介面與IndicationInstance非常類似:它會公開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。不過,如果極少數情況下,您是使用 rememberUpdatedInstance 手動建立 IndicationInstance,則需要更新導入作業,檢查 Indication 是否為 IndicationNodeFactory,以便使用較輕量的導入作業。舉例來說,如果 Modifier.indication 是 IndicationNodeFactory,系統會在內部委派給建立的節點。否則,系統會使用 Modifier.composed 呼叫 rememberUpdatedInstance。