Zur Verbesserung der Zusammensetzungsleistung interaktiver Komponenten, die
Modifier.clickable
, wir haben neue APIs eingeführt. Diese APIs ermöglichen mehr
effiziente Indication
-Implementierungen wie Ripples.
androidx.compose.foundation:foundation:1.7.0+
und
androidx.compose.material:material-ripple:1.7.0+
enthalten die folgende API
Änderungen:
Eingestellt |
Ersatz |
---|---|
|
|
|
Stattdessen werden neue Hinweis: In diesem Kontext bezieht sich auf |
|
Entweder:
|
Auf dieser Seite werden die Auswirkungen der Verhaltensänderung sowie eine Anleitung für die Migration zu für die neuen APIs.
Verhaltensänderung
Die folgenden Bibliotheksversionen enthalten eine Änderung im Ripple-Verhalten:
androidx.compose.material:material:1.7.0+
androidx.compose.material3:material3:1.3.0+
androidx.wear.compose:compose-material:1.4.0+
In diesen Versionen von Material Library wird rememberRipple()
nicht mehr verwendet. verwenden Sie stattdessen
nutzen sie die neuen Ripple-APIs. Daher wird LocalRippleTheme
nicht abgefragt.
Wenn Sie also in Ihrer Anwendung LocalRippleTheme
festlegen, wird Material
Komponenten verwenden diese Werte nicht.
Im folgenden Abschnitt wird beschrieben, wie Sie vorübergehend auf das alte Verhalten zurückgreifen können.
ohne zu migrieren; Wir empfehlen jedoch die Migration zu den neuen APIs. Für
Eine Migrationsanleitung finden Sie unter Von rememberRipple
zu ripple
migrieren.
und den nachfolgenden Abschnitten.
Version der Material-Bibliothek ohne Migration aktualisieren
Die Blockierung von Upgrades der Bibliotheksversionen aufheben, indem Sie die temporäre
Zu konfigurierende LocalUseFallbackRippleImplementation CompositionLocal
API
Materialkomponenten, die auf das alte Verhalten zurückgreifen:
CompositionLocalProvider(LocalUseFallbackRippleImplementation provides true) { MaterialTheme { App() } }
Stellen Sie dies außerhalb von MaterialTheme
bereit, damit die alten Wellen
über LocalIndication
bereitgestellt werden.
In den folgenden Abschnitten wird die Migration zu den neuen APIs beschrieben.
Von rememberRipple
zu ripple
migrieren
Material-Bibliothek verwenden
Wenn Sie eine Material-Bibliothek verwenden, ersetzen Sie rememberRipple()
direkt durch einen
ripple()
aus der entsprechenden Bibliothek aufrufen. Diese API erstellt eine Welle
unter Verwendung von Werten, die aus den Material Theme-APIs abgeleitet wurden. Übergeben Sie dann die zurückgegebene
Objekt für Modifier.clickable
und/oder andere Komponenten.
Im folgenden Snippet werden beispielsweise die eingestellten APIs verwendet:
Box( Modifier.clickable( onClick = {}, interactionSource = remember { MutableInteractionSource() }, indication = rememberRipple() ) ) { // ... }
Sie sollten das obige Snippet wie folgt ändern:
@Composable private fun RippleExample() { Box( Modifier.clickable( onClick = {}, interactionSource = remember { MutableInteractionSource() }, indication = ripple() ) ) { // ... } }
ripple()
ist keine zusammensetzbare Funktion mehr und muss nicht
gespeichert werden. Es kann auch über mehrere Komponenten hinweg wiederverwendet werden, ähnlich wie
Modifizierer. Extrahieren Sie daher die Wellenbildung in einen Wert auf oberster Ebene,
Zuweisungen speichern.
Implementierung eines kundenspezifischen Designsystems
Wenn Sie Ihr eigenes Designsystem implementieren und zuvor
rememberRipple()
zusammen mit einem benutzerdefinierten RippleTheme
zum Konfigurieren der Verbreitung.
sollten Sie stattdessen Ihre eigene Ripple-API bereitstellen, die an den Ripple-Knoten delegiert
In material-ripple
bereitgestellte APIs. Dann können Ihre Komponenten Ihre eigene Welle verwenden.
die direkt auf die Werte Ihres Themas angerechnet wird. Weitere Informationen finden Sie unter Migrieren
aus RippleTheme
.
Von RippleTheme
migrieren
Verhaltensänderung vorübergehend deaktivieren
Materialbibliotheken haben eine temporäre CompositionLocal
,
LocalUseFallbackRippleImplementation
, mit dem Sie alle
Materialkomponenten, für die rememberRipple
verwendet werden soll. Auf diese Weise
rememberRipple
fragt weiterhin LocalRippleTheme
ab.
Das folgende Code-Snippet zeigt, wie das Tag
LocalUseFallbackRippleImplementation CompositionLocal
-API:
CompositionLocalProvider(LocalUseFallbackRippleImplementation provides true) { MaterialTheme { App() } }
Wenn Sie ein benutzerdefiniertes App-Design verwenden, das auf Material aufbaut, können Sie Stellen Sie die Komposition sicher als Teil des Designs Ihrer App bereit:
@OptIn(ExperimentalMaterialApi::class) @Composable fun MyAppTheme(content: @Composable () -> Unit) { CompositionLocalProvider(LocalUseFallbackRippleImplementation provides true) { MaterialTheme(content = content) } }
Weitere Informationen finden Sie unter Upgrade von Material-Bibliothek-Version ohne Migration.
Mit RippleTheme
eine Welle für eine bestimmte Komponente deaktivieren
Die Bibliotheken material
und material3
machen RippleConfiguration
und
LocalRippleConfiguration
, mit denen Sie die Darstellung von
in einer Unterstruktur. Beachten Sie, dass RippleConfiguration
und
LocalRippleConfiguration
sind experimentell und nur für die Verwendung pro Komponente vorgesehen
Personalisierung. Die globale/themenweite Anpassung wird hierbei nicht unterstützt.
APIs Siehe Mit RippleTheme
alle Ripples-Daten global ändern
finden Sie weitere Informationen zu diesem Anwendungsfall.
Im folgenden Snippet werden beispielsweise die eingestellten APIs verwendet:
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 { // ... } }
Sie sollten das obige Snippet wie folgt ändern:
CompositionLocalProvider(LocalRippleConfiguration provides null) { Button { // ... } }
RippleTheme
verwenden, um die Farbe/den Alphawert einer Welle für eine bestimmte Komponente zu ändern
Wie im vorherigen Abschnitt beschrieben, werden RippleConfiguration
und
LocalRippleConfiguration
sind experimentelle APIs, die nur für
für die einzelnen Komponenten.
Im folgenden Snippet werden beispielsweise die eingestellten APIs verwendet:
private object DisabledRippleThemeColorAndAlpha : RippleTheme { @Composable override fun defaultColor(): Color = Color.Red @Composable override fun rippleAlpha(): RippleAlpha = MyRippleAlpha } // ... CompositionLocalProvider(LocalRippleTheme provides DisabledRippleThemeColorAndAlpha) { Button { // ... } }
Sie sollten das obige Snippet wie folgt ändern:
@OptIn(ExperimentalMaterialApi::class) private val MyRippleConfiguration = RippleConfiguration(color = Color.Red, rippleAlpha = MyRippleAlpha) // ... CompositionLocalProvider(LocalRippleConfiguration provides MyRippleConfiguration) { Button { // ... } }
Mit RippleTheme
alle Ripples in einer Anwendung global ändern
Bisher konnten Sie mit LocalRippleTheme
das Wellenverhalten an einem
für das gesamte Thema gelten. Dies war im Wesentlichen ein Integrationspunkt zwischen benutzerdefinierten
Designsystem von lokalen und Wellen. Anstelle eines allgemeinen
nach dem thematischen Primitiv zeichnen, stellt material-ripple
jetzt eine createRippleModifierNode()
. Mit dieser Funktion können Designsystembibliotheken
wrapper
-Implementierung anzuordnen, die ihre Themenwerte abfragen und dann
die Ripple-Implementierung auf dem Knoten, der von dieser Funktion erstellt wird.
So können Designsysteme
benötigte Informationen direkt abfragen
erforderten vom Nutzer konfigurierbare Design-Ebenen, ohne
was in der material-ripple
-Ebene bereitgestellt wird. Diese Änderung sorgt außerdem für mehr
dem Thema/der Spezifikation der Welle entspricht,
Ripple API selbst, die diesen Vertrag definiert, anstatt implizit
aus dem Thema abgeleitet wird.
Weitere Informationen findest du in der Ripple API-Implementierung in Material und ersetzen Sie die Aufrufe von Material Ihr eigenes Designsystem nutzen.
Von Indication
zu IndicationNodeFactory
migrieren
Um Indication
zu passieren
Wenn Sie nur ein Indication
zur Weitergabe erstellen, also z. B. ein
an Modifier.clickable
oder Modifier.indication
übergeben werden,
Änderungen vornehmen müssen. IndicationNodeFactory
übernimmt von Indication
,
sodass alles kompiliert wird und funktioniert.
Indication
wird erstellt
Wenn Sie Ihre eigene Indication
-Implementierung erstellen, sollte die Migration
in den meisten Fällen einfach sein. Stellen Sie sich z. B. einen Indication
vor, der
Skalierungseffekt beim Drücken:
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() } } }
Für die Migration sind zwei Schritte erforderlich:
Migrieren Sie
ScaleIndicationInstance
zu einerDrawModifierNode
. Die API-Oberfläche fürDrawModifierNode
istIndicationInstance
sehr ähnlich: Sie stellt eineContentDrawScope#draw()
, die funktional äquivalent zuIndicationInstance#drawContent()
. Sie müssen diese Funktion ändern implementieren Sie diecollectLatest
-Logik direkt im Knoten anstelle desIndication
.Im folgenden Snippet werden beispielsweise die eingestellten APIs verwendet:
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() } } }
Sie sollten das obige Snippet wie folgt ändern:
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() } } }
Migrieren Sie
ScaleIndication
, umIndicationNodeFactory
zu implementieren. Da die wird in den Knoten verschoben. Dies ist eine sehr einfache Fabrik, Objekt, dessen einzige Aufgabe darin besteht, eine Knoteninstanz zu erstellen.Im folgenden Snippet werden beispielsweise die eingestellten APIs verwendet:
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 } }
Sie sollten das obige Snippet wie folgt ändern:
object ScaleIndicationNodeFactory : IndicationNodeFactory { override fun create(interactionSource: InteractionSource): DelegatableNode { return ScaleIndicationNode(interactionSource) } override fun hashCode(): Int = -1 override fun equals(other: Any?) = other === this }
IndicationInstance
mit Indication
erstellen
In den meisten Fällen sollten Sie Modifier.indication
verwenden, um Indication
für eine
Komponente. In dem seltenen Fall, dass Sie manuell eine
IndicationInstance
verwendet rememberUpdatedInstance
, du musst deine
implementieren, um zu prüfen, ob Indication
ein IndicationNodeFactory
ist, sodass Sie
können Sie eine leichtere
Implementierung verwenden. Beispiel: Modifier.indication
wird
Delegieren Sie intern an den erstellten Knoten, wenn es sich um einen IndicationNodeFactory
handelt. Wenn
nicht, wird Modifier.composed
verwendet, um rememberUpdatedInstance
aufzurufen.