Indication API と 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()

代わりにマテリアル ライブラリで新しい ripple() API が提供されます。

注: ここでは「マテリアル ライブラリ」、androidx.compose.material:materialandroidx.compose.material3:material3androidx.wear.compose:compose-materialandroidx.wear.compose:compose-material3. を指します。

RippleTheme

Either:

  • マテリアル ライブラリの RippleConfiguration API を使用する。
  • 独自のデザイン システムのリップル実装を構築する

このページでは、動作変更による影響と、 説明します。

動作の変更

次のライブラリ バージョンでは、リップルの動作が変更されています。

  • androidx.compose.material:material:1.7.0+
  • androidx.compose.material3:material3:1.3.0+
  • androidx.wear.compose:compose-material:1.4.0+

これらのバージョンのマテリアル ライブラリでは rememberRipple() が使用されなくなりました。使用する代わりに、 新しい Ripple API を使用していますそのため、LocalRippleTheme はクエリされません。 そのため、アプリで LocalRippleTheme を設定すると、Material これらの値は使用されません

次のセクションでは、以前の動作に一時的にフォールバックする方法について説明します。 できます。新しい API に移行することをおすすめします。対象 移行手順については、rememberRipple から ripple への移行をご覧ください。 説明します。

移行せずにマテリアル ライブラリのバージョンをアップグレードする

ライブラリ バージョンのアップグレードのブロックを解除するには、一時的な 構成する LocalUseFallbackRippleImplementation CompositionLocal API 以前の動作にフォールバックするためのマテリアル コンポーネント:

CompositionLocalProvider(LocalUseFallbackRippleImplementation provides true) {
    MaterialTheme {
        App()
    }
}

古いさざ波が残るように、必ず MaterialThemeに配置してください LocalIndication を介して提供されます。

以降のセクションでは、新しい API に移行する方法について説明します。

rememberRipple から ripple に移行する

マテリアル ライブラリの使用

マテリアル ライブラリを使用している場合は、rememberRipple() を直接 対応するライブラリから ripple() を呼び出します。この API はさまざまな Material Theme 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。コンポーネントは独自のリップルを使用して テーマ値を直接消費します詳細については、Migrate to Migrate RippleTheme から

RippleTheme から移行する

動作変更を一時的にオプトアウトする

マテリアル ライブラリには一時的な CompositionLocal があります。 LocalUseFallbackRippleImplementation: これを使用して、 rememberRipple を使用するようフォールバックするマテリアル コンポーネント。このように rememberRippleLocalRippleTheme のクエリを続行します。

次のコード スニペットは、LocalUseFallbackRippleImplementation CompositionLocal API の使用方法を示しています。

CompositionLocalProvider(LocalUseFallbackRippleImplementation provides true) {
    MaterialTheme {
        App()
    }
}

マテリアルを基に構築されたカスタムアプリテーマを使用している場合は、次のことができます。 アプリのテーマの一部としてコンポジション ローカルを安全に提供します。

@OptIn(ExperimentalMaterialApi::class)
@Composable
fun MyAppTheme(content: @Composable () -> Unit) {
    CompositionLocalProvider(LocalUseFallbackRippleImplementation provides true) {
        MaterialTheme(content = content)
    }
}

詳しくは、Android Vitals を使用せずにマテリアル ライブラリ バージョンをアップグレードするには、 移行セクションを参照してください。

RippleTheme を使用して特定のコンポーネントのリップルを無効にする

material ライブラリと material3 ライブラリは、RippleConfigurationLocalRippleConfiguration を使用すると、アプリのデザインを 影響します。RippleConfigurationLocalRippleConfiguration は試験運用版で、コンポーネントごとの カスタマイズが可能です。これらのプロダクトでは、グローバル/テーマ全体のカスタマイズはサポートされていません APIRippleTheme を使用してすべてのリップルをグローバルに変更する をご覧ください。

たとえば、次のスニペットでは非推奨の 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 を使用して特定のコンポーネントのリップルの色/アルファを変更する

前のセクションで説明したように、RippleConfigurationLocalRippleConfiguration は試験運用版の 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 を使用して、特定の時点での波紋効果を定義して できます。これは基本的に、カスタム システム コンポジションのローカルとリップルを設計します。汎用の API を公開するのではなく、 テーマ設定プリミティブの material-ripplecreateRippleModifierNode() を公開するようになりました 使用します。この関数を使用すると、デザイン システム ライブラリでより高度な テーマ値をクエリしてデリゲートする wrapper 実装を順序付けする リップル実装を、この関数で作成されたノードに送ります。

これにより、設計システムは必要なものを直接クエリして、 ユーザー設定が可能なテーマ設定レイヤを一番上に置いておく必要があります。 material-ripple レイヤで提供されます。この変更により その波形がどのテーマ/仕様に準拠しているかを明示する 暗黙的にではなく、コントラクトを定義する 作成します。

詳しくは、マテリアルの Ripple API の実装をご覧ください 必要に応じてマテリアル コンポジション ローカルの呼び出しを 独自のデザイン システムを作成できます。

Indication から IndicationNodeFactory に移行する

Indication 付近を通過中

渡すために Indication を作成するだけの場合(たとえば、 Modifier.clickable または Modifier.indication に波及していくと、 変更が必要です。IndicationNodeFactoryIndication から継承し、 すべてが問題なくコンパイルされ、機能します。

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()
        }
    }
}

これは次の 2 つのステップで移行できます。

  1. ScaleIndicationInstanceDrawModifierNode に移行します。API サーフェス DrawModifierNode のメソッドは 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()
            }
        }
    }

  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 を 説明します。ただし、まれなケースですが、サービス アカウントを IndicationInstancerememberUpdatedInstance を使用しています。更新する必要があります IndicationIndicationNodeFactory であるかどうかをチェックし、 より軽量な実装を使用できます。たとえば、Modifier.indication は、 作成されたノードが IndicationNodeFactory の場合、内部で委任します。条件 そうでない場合は、Modifier.composed を使用して rememberUpdatedInstance を呼び出します。