Pour améliorer les performances de composition des composants interactifs qui utilisent
Modifier.clickable
, nous avons introduit de nouvelles API. Ces API vous permettent
des implémentations Indication
efficaces, telles que les ondulations.
androidx.compose.foundation:foundation:1.7.0+
et
Les androidx.compose.material:material-ripple:1.7.0+
incluent l'API suivante
modifications:
Obsolète |
Remplacement |
---|---|
|
|
|
Nouvelles API Remarque: Dans ce contexte, le terme "bibliothèques Material" fait référence à |
|
Deux options s'offrent à vous :
|
Cette page décrit l'impact des modifications de comportement et fournit des instructions pour migrer vers les nouvelles API.
Changement de comportement
Les versions suivantes de la bibliothèque incluent un changement de comportement d'ondulation:
androidx.compose.material:material:1.7.0+
androidx.compose.material3:material3:1.3.0+
androidx.wear.compose:compose-material:1.4.0+
Ces versions des bibliothèques Material n'utilisent plus rememberRipple()
. à la place,
ils utilisent les nouvelles API Ripple. Par conséquent, ils n'interrogent pas LocalRippleTheme
.
Par conséquent, si vous définissez LocalRippleTheme
dans votre application, Material
n'utilisent pas ces valeurs.
La section suivante explique comment revenir temporairement à l'ancien comportement
sans migrer, Toutefois, nous vous recommandons de migrer vers les nouvelles API. Pour
pour obtenir des instructions sur la migration, consultez Migrer de rememberRipple
vers ripple
.
et les sections suivantes.
Mettre à niveau la version de la bibliothèque Material sans effectuer de migration
Pour débloquer la mise à niveau des versions de la bibliothèque, vous pouvez utiliser le
API LocalUseFallbackRippleImplementation CompositionLocal
à configurer
Les composants Material reprennent l'ancien comportement:
CompositionLocalProvider(LocalUseFallbackRippleImplementation provides true) { MaterialTheme { App() } }
Veillez à le fournir en dehors de MaterialTheme
pour que les anciennes ondulations puissent
être fourni via LocalIndication
.
Dans les sections suivantes, nous allons voir comment migrer vers les nouvelles API.
Migrer de rememberRipple
vers ripple
Utiliser une bibliothèque Material
Si vous utilisez une bibliothèque Material, remplacez directement rememberRipple()
par un
un appel à ripple()
à partir de la bibliothèque correspondante. Cette API crée une ondulation
à l'aide de valeurs dérivées des API Material Theme. Ensuite, transmettez la valeur renvoyée
à Modifier.clickable
et/ou à d'autres composants.
Par exemple, l'extrait de code suivant utilise les API obsolètes:
Box( Modifier.clickable( onClick = {}, interactionSource = remember { MutableInteractionSource() }, indication = rememberRipple() ) ) { // ... }
Vous devez modifier l'extrait ci-dessus de la manière suivante:
@Composable private fun RippleExample() { Box( Modifier.clickable( onClick = {}, interactionSource = remember { MutableInteractionSource() }, indication = ripple() ) ) { // ... } }
Notez que ripple()
n'est plus une fonction modulable et n'a pas besoin d'être
mémorisé. Il peut également être réutilisé pour plusieurs composants,
des modificateurs. Par conséquent, envisagez d'extraire la création de l'ondulation à une valeur de niveau supérieur pour
enregistrer les allocations.
Implémenter un système de conception personnalisé
Si vous mettez en œuvre votre propre
système de conception et que vous utilisiez auparavant
rememberRipple()
avec un RippleTheme
personnalisé pour configurer l'ondulation
À la place, fournissez votre propre API Ripple qui délègue au nœud Ripple
API exposées dans material-ripple
. Vos composants peuvent ensuite utiliser votre propre ondulation
qui utilise directement les valeurs de votre thème. Pour en savoir plus, consultez la section Migrer
de RippleTheme
.
Migrer depuis RippleTheme
Désactiver temporairement le changement de comportement
Les bibliothèques Material ont un CompositionLocal
temporaire,
LocalUseFallbackRippleImplementation
, que vous pouvez utiliser pour configurer
Composants Material à utiliser en cas d'utilisation de rememberRipple
. De cette façon,
rememberRipple
continue d'interroger LocalRippleTheme
.
L'extrait de code suivant montre comment utiliser la classe
API LocalUseFallbackRippleImplementation CompositionLocal
:
CompositionLocalProvider(LocalUseFallbackRippleImplementation provides true) { MaterialTheme { App() } }
Si vous utilisez un thème d'application personnalisé basé sur Material, vous pouvez fournir en toute sécurité la composition locale dans le thème de votre application:
@OptIn(ExperimentalMaterialApi::class) @Composable fun MyAppTheme(content: @Composable () -> Unit) { CompositionLocalProvider(LocalUseFallbackRippleImplementation provides true) { MaterialTheme(content = content) } }
Pour en savoir plus, consultez Mettre à niveau la version de la bibliothèque Material sans migration.
Utiliser RippleTheme
pour désactiver une ondulation pour un composant donné
Les bibliothèques material
et material3
exposent RippleConfiguration
et
LocalRippleConfiguration
, qui vous permettent de configurer l'apparence
ondulations dans
une sous-arborescence. Notez que RippleConfiguration
et
Les LocalRippleConfiguration
sont expérimentales et ne sont destinées qu'à des composants spécifiques
la personnalisation. La personnalisation globale/à l'échelle du thème n'est pas prise en charge avec ces
les API ; consultez la section Utiliser RippleTheme
pour modifier globalement toutes les ondulations d'une
application pour en savoir plus sur ce cas d'utilisation.
Par exemple, l'extrait de code suivant utilise les API obsolètes:
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 { // ... } }
Vous devez modifier l'extrait ci-dessus de la manière suivante:
CompositionLocalProvider(LocalRippleConfiguration provides null) { Button { // ... } }
Utiliser RippleTheme
pour modifier la couleur/l'alpha d'une ondulation pour un composant donné
Comme décrit dans la section précédente, RippleConfiguration
et
Les API LocalRippleConfiguration
sont expérimentales et ne sont destinées qu'à
personnalisation par composant.
Par exemple, l'extrait de code suivant utilise les API obsolètes:
private object DisabledRippleThemeColorAndAlpha : RippleTheme { @Composable override fun defaultColor(): Color = Color.Red @Composable override fun rippleAlpha(): RippleAlpha = MyRippleAlpha } // ... CompositionLocalProvider(LocalRippleTheme provides DisabledRippleThemeColorAndAlpha) { Button { // ... } }
Vous devez modifier l'extrait ci-dessus de la manière suivante:
@OptIn(ExperimentalMaterialApi::class) private val MyRippleConfiguration = RippleConfiguration(color = Color.Red, rippleAlpha = MyRippleAlpha) // ... CompositionLocalProvider(LocalRippleConfiguration provides MyRippleConfiguration) { Button { // ... } }
Utiliser RippleTheme
pour modifier globalement toutes les ondulations d'une application
Auparavant, vous pouviez utiliser LocalRippleTheme
pour définir le comportement de l'ondulation à un niveau
à l'échelle du thème. Il s'agissait essentiellement d'un point d'intégration
la composition locale du système
de conception et l'ondulation. Au lieu d'exposer une image
primitive de thématisation, material-ripple
expose désormais une createRippleModifierNode()
. Cette fonction permet aux bibliothèques de systèmes de conception de créer
commandent l'implémentation de wrapper
, qui interrogent leurs valeurs de thème, puis délèguent
l'implémentation de l'ondulation sur le nœud créé par cette fonction.
Cela permet aux systèmes de conception d'interroger directement ce dont ils ont besoin et d'exposer tout
nécessite des couches de thématisation configurables par l'utilisateur au-dessus, sans avoir à se conformer
ce qui est fourni au niveau de la couche material-ripple
. Ce changement rend également plus
explicite du thème ou de la spécification auxquels l'ondulation se conforme, car il s'agit
qui définit ce contrat, plutôt que d'être implicitement
en fonction du thème.
Pour obtenir des conseils, consultez la section Implémentation de l'API Ripple dans Material Design. et de remplacer les appels aux composants locaux de la composition Material si nécessaire pour votre propre système de conception.
Migrer de Indication
vers IndicationNodeFactory
Contournement de Indication
Si vous créez simplement un Indication
à transmettre, par exemple en créant une
pour être transmis à Modifier.clickable
ou Modifier.indication
,
n'ont pas besoin d'apporter des modifications. IndicationNodeFactory
hérite de Indication
,
afin que tout continue à
se compiler et à fonctionner.
Création de Indication
...
Si vous créez votre propre implémentation de Indication
, la migration doit
être simple dans la plupart des cas. Prenons l'exemple d'un Indication
qui applique une
Effet de mise à l'échelle lorsque l'utilisateur appuie:
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() } } }
Vous pouvez effectuer la migration en deux étapes:
Migrez
ScaleIndicationInstance
pour devenir unDrawModifierNode
. Surface de l'API pourDrawModifierNode
est très semblable àIndicationInstance
: il expose un fonctionContentDrawScope#draw()
fonctionnellement équivalente àIndicationInstance#drawContent()
Vous devez modifier cette fonction, puis implémenter la logiquecollectLatest
directement à l'intérieur du nœud, au lieu deIndication
Par exemple, l'extrait de code suivant utilise les API obsolètes:
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() } } }
Vous devez modifier l'extrait ci-dessus de la manière suivante:
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() } } }
Migrez
ScaleIndication
pour implémenterIndicationNodeFactory
. En effet, la logique de collecte est déplacée dans le nœud, il s'agit d'une fabrique très simple dont la seule responsabilité est de créer une instance de nœud.Par exemple, l'extrait de code suivant utilise les API obsolètes:
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 } }
Vous devez modifier l'extrait ci-dessus de la manière suivante:
object ScaleIndicationNodeFactory : IndicationNodeFactory { override fun create(interactionSource: InteractionSource): DelegatableNode { return ScaleIndicationNode(interactionSource) } override fun hashCode(): Int = -1 override fun equals(other: Any?) = other === this }
Utiliser Indication
pour créer un IndicationInstance
Dans la plupart des cas, vous devez utiliser Modifier.indication
pour afficher Indication
pour une
. Toutefois, dans les rares cas où vous créeriez manuellement un
IndicationInstance
avec rememberUpdatedInstance
, vous devez mettre à jour votre
pour vérifier si Indication
est de type IndicationNodeFactory
.
utiliser une implémentation plus légère. Par exemple, Modifier.indication
va
déléguer en interne au nœud créé s'il s'agit d'un IndicationNodeFactory
. Si
non, il utilisera Modifier.composed
pour appeler rememberUpdatedInstance
.