為提升使用 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
。