Modifier.clickable
を使用するインタラクティブ コンポーネントのコンポジション パフォーマンスを改善するために、新しい API が導入されました。これらの API を使用すると、リップルなど、より効率的な Indication
実装が可能になります。
androidx.compose.foundation:foundation:1.7.0+
と androidx.compose.material:material-ripple:1.7.0+
には、次の API の変更が含まれています。
非推奨 |
置き換え |
---|---|
|
|
|
代わりに、マテリアル ライブラリで新しい 注: ここで「マテリアル ライブラリ」とは、 |
|
次のいずれかの手順を行います。
|
このページでは、動作変更の影響と、新しい API への移行手順について説明します。
動作の変更
次のライブラリ バージョンには、リップル動作の変更が含まれています。
androidx.compose.material:material:1.7.0+
androidx.compose.material3:material3:1.3.0+
androidx.wear.compose:compose-material:1.4.0+
これらのバージョンのマテリアル ライブラリでは、rememberRipple()
は使用されなくなり、代わりに新しいリップル API が使用されます。その結果、LocalRippleTheme
のクエリは行われません。そのため、アプリケーションで LocalRippleTheme
を設定しても、マテリアル コンポーネントはこれらの値を使用しません。
以降のセクションでは、新しい API に移行する方法について説明します。
rememberRipple
から ripple
に移行する
マテリアル ライブラリを使用する
マテリアル ライブラリを使用している場合は、rememberRipple()
を対応するライブラリの ripple()
の呼び出しに直接置き換えます。この API は、マテリアル テーマ 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
を使用してリップルを構成していた場合は、代わりに material-ripple
で公開されているリップルノード API に委任する独自のリップル 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
を使用して、特定のコンポーネントのリップルの色/アルファを変更する
前のセクションで説明したように、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 自体がコントラクトを定義するため、リップルが準拠するテーマ/仕様がより明示的になります。
ガイダンスについては、マテリアル ライブラリのリップル API の実装を参照し、必要に応じて、独自のデザイン システムに合わせてマテリアル コンポジション ローカルの呼び出しを置き換えてください。
Indication
から IndicationNodeFactory
に移行する
Indication
を渡す
Modifier.clickable
や Modifier.indication
に渡すリップルを作成するなど、渡すだけの 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() } } }
この移行は次の 2 つの手順で行います。
ScaleIndicationInstance
をDrawModifierNode
に移行します。DrawModifierNode
の API サーフェスはIndicationInstance
と非常によく似ています。IndicationInstance#drawContent()
と機能的に同等のContentDrawScope#draw()
関数を公開します。この関数を変更し、Indication
ではなく、ノード内でcollectLatest
ロジックを直接実装する必要があります。たとえば、次のスニペットでは非推奨の 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
を呼び出します。