遷移至 Indication 和 Ripple API

為了針對使用 Modifier.clickable,我們推出了新的 API。這些 API 高效率的 Indication 實作方式,例如漣漪效果。

androidx.compose.foundation:foundation:1.7.0+」和 androidx.compose.material:material-ripple:1.7.0+ 包含下列 API 變更:

已淘汰

取代選項

Indication#rememberUpdatedInstance

IndicationNodeFactory

rememberRipple()

請改為在 Material 程式庫中提供新的 ripple() API。

注意:在本文中,「Material Library」涵蓋 androidx.compose.material:materialandroidx.compose.material3:material3androidx.wear.compose:compose-materialandroidx.wear.compose:compose-material3.

RippleTheme

  • 使用 Material 程式庫 RippleConfiguration API,或
  • 建構自己的設計系統漣漪效果

本頁面說明行為變更的影響,以及遷移至 BigQuery 的操作說明。 新的 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。 因此,如果您在應用程式中設定 LocalRippleThemeMaterial 元件不會使用這些值

下一節將說明如何暫時改回使用舊行為 而不進行遷移不過,建議您改用新的 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 Design 主題 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 程式庫具有臨時的 CompositionLocalLocalUseFallbackRippleImplementation,可用於設定 改回使用 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)
    }
}

詳情請參閱在以下情況中升級 Material Design 程式庫版本: 遷移一節。

使用 RippleTheme 停用特定元件的漣漪效果

materialmaterial3 程式庫會公開 RippleConfigurationLocalRippleConfiguration,可讓您設定 子樹狀結構中的漣漪。請注意,RippleConfigurationLocalRippleConfiguration 屬於實驗功能,僅適用於個別元件 。全域/主題自訂功能不適用於以下項目 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 層的作用範圍這項變更也帶來 明確地呈現漣漪效果的主題/規格,因為 則會定義該合約,而不是隱含

如需相關指南,請參閱 Material Design 中的 漣漪 API 實作 程式庫,並視需要取代對 Material 組合本機元件的呼叫 您打造自己的設計系統

從「Indication」遷移至「IndicationNodeFactory

繞過Indication

如果您只是建立要傳遞的 Indication (例如建立 用來傳遞到 Modifier.clickableModifier.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()
        }
    }
}

您可以透過兩個步驟來遷移這項設定:

  1. ScaleIndicationInstance 遷移至 DrawModifierNode。API 介面 DrawModifierNodeIndicationInstance 非常相似,其會顯示 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()
            }
        }
    }

  2. 遷移 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 元件。不過,在極少數的情況下,您必須手動建立 使用 rememberUpdatedInstanceIndicationInstance,請更新您的 可以檢查 Indication 是否為 IndicationNodeFactory,因此您可以 較簡單的實作程序舉例來說,Modifier.indication 會 如果節點是 IndicationNodeFactory,則會在內部委派給已建立的節點。如果 而不是,它會使用 Modifier.composed 呼叫 rememberUpdatedInstance