Per migliorare le prestazioni della composizione dei componenti interattivi che utilizzano
Modifier.clickable
, abbiamo introdotto nuove API. Queste API consentono di avere
implementazioni Indication
efficienti, come le eco.
androidx.compose.foundation:foundation:1.7.0+
e
androidx.compose.material:material-ripple:1.7.0+
includono la seguente API
modifiche:
Obsoleta |
Sostituzione |
---|---|
|
|
|
Nuove API Nota: in questo contesto, le "librerie di materiali" si riferisce a |
|
Procedi in uno dei seguenti modi:
|
Questa pagina descrive l'impatto delle modifiche del comportamento e le istruzioni per eseguire la migrazione a le nuove API.
Cambiamento del comportamento
Le seguenti versioni della libreria includono una modifica del comportamento dell'eco:
androidx.compose.material:material:1.7.0+
androidx.compose.material3:material3:1.3.0+
androidx.wear.compose:compose-material:1.4.0+
Queste versioni delle librerie Material non utilizzano più rememberRipple()
; invece,
usano le nuove API Ripple. Di conseguenza, non eseguono query su LocalRippleTheme
.
Pertanto, se imposti LocalRippleTheme
nella tua applicazione, Materiale
non utilizzeranno questi valori.
La seguente sezione descrive come tornare temporaneamente al vecchio comportamento
senza eseguire la migrazione; tuttavia, consigliamo di eseguire la migrazione alle nuove API. Per
istruzioni per la migrazione, vedi Eseguire la migrazione da rememberRipple
a ripple
e nelle sezioni successive.
Esegui l'upgrade della versione della libreria di materiali senza eseguire la migrazione
Per sbloccare l'upgrade delle versioni della libreria, puoi utilizzare la
LocalUseFallbackRippleImplementation CompositionLocal
API da configurare
Componenti materiali su cui basare il vecchio comportamento:
CompositionLocalProvider(LocalUseFallbackRippleImplementation provides true) { MaterialTheme { App() } }
Assicurati di fornire questo codice al di fuori di MaterialTheme
in modo che le vecchie onde possano
essere fornito tramite LocalIndication
.
Le sezioni seguenti descrivono come eseguire la migrazione alle nuove API.
Esegui la migrazione da rememberRipple
a ripple
Uso di una libreria di materiali
Se utilizzi una libreria di materiali, sostituisci direttamente rememberRipple()
con un
chiamata a ripple()
dalla libreria corrispondente. Questa API crea un'eco
utilizzando valori derivati dalle API Material
Tema. Quindi, passa il token
a Modifier.clickable
e/o ad altri componenti.
Ad esempio, il seguente snippet utilizza le API ritirate:
Box( Modifier.clickable( onClick = {}, interactionSource = remember { MutableInteractionSource() }, indication = rememberRipple() ) ) { // ... }
Devi modificare lo snippet riportato sopra per:
@Composable private fun RippleExample() { Box( Modifier.clickable( onClick = {}, interactionSource = remember { MutableInteractionSource() }, indication = ripple() ) ) { // ... } }
Tieni presente che ripple()
non è più una funzione componibile e non deve essere
ricordato. Può essere riutilizzato anche in più componenti, come
modificatori, quindi considera la possibilità di estrarre la creazione dell'onda in un valore di primo livello
salvare le allocazioni.
Implementazione di un sistema di progettazione personalizzato
Se implementi un tuo sistema di progettazione e in precedenza utilizzavi
rememberRipple()
insieme a un RippleTheme
personalizzato per configurare l'eco,
devi invece fornire la tua API Ripple che delega al nodo Ripple
API esposte in material-ripple
. I componenti possono quindi utilizzare la tua eco
che utilizza direttamente i valori del tuo tema. Per ulteriori informazioni, consulta Eseguire la migrazione
da RippleTheme
.
Esegui la migrazione da RippleTheme
Disattiva temporaneamente la modifica del comportamento
Le librerie di materiali hanno un CompositionLocal
temporaneo,
LocalUseFallbackRippleImplementation
, che puoi usare per configurare tutte
Componenti dei materiali su cui ricorrere a rememberRipple
. In questo modo
rememberRipple
continua a eseguire la query su LocalRippleTheme
.
Il seguente snippet di codice illustra come utilizzare il parametro
API LocalUseFallbackRippleImplementation CompositionLocal
:
CompositionLocalProvider(LocalUseFallbackRippleImplementation provides true) { MaterialTheme { App() } }
Se utilizzi un tema personalizzato dell'app basato su Material, puoi fornire in modo sicuro la composizione locale come parte del tema della tua app:
@OptIn(ExperimentalMaterialApi::class) @Composable fun MyAppTheme(content: @Composable () -> Unit) { CompositionLocalProvider(LocalUseFallbackRippleImplementation provides true) { MaterialTheme(content = content) } }
Per ulteriori informazioni, consulta la sezione Eseguire l'upgrade della versione della libreria di materiali senza migrazione.
Utilizzo di RippleTheme
per disattivare un ripple per un determinato componente
Le librerie material
e material3
espongono RippleConfiguration
e
LocalRippleConfiguration
, che ti consentono di configurare l'aspetto
increspature in un sottoalbero. Tieni presente che RippleConfiguration
e
I LocalRippleConfiguration
sono sperimentali e destinati esclusivamente all'utilizzo a livello di singolo componente
personalizzazione. La personalizzazione globale/a livello di tema non è supportata con questi
le API consulta Utilizzare RippleTheme
per modificare a livello globale tutte le eco in una
applicazione per ulteriori informazioni sul caso d'uso specifico.
Ad esempio, il seguente snippet utilizza le API ritirate:
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 { // ... } }
Devi modificare lo snippet riportato sopra per:
CompositionLocalProvider(LocalRippleConfiguration provides null) { Button { // ... } }
Utilizzo di RippleTheme
per modificare il colore/alfa di un'ondata per un determinato componente
Come descritto nella sezione precedente, RippleConfiguration
e
LocalRippleConfiguration
sono API sperimentali e sono destinate esclusivamente alla
la personalizzazione per componente.
Ad esempio, il seguente snippet utilizza le API ritirate:
private object DisabledRippleThemeColorAndAlpha : RippleTheme { @Composable override fun defaultColor(): Color = Color.Red @Composable override fun rippleAlpha(): RippleAlpha = MyRippleAlpha } // ... CompositionLocalProvider(LocalRippleTheme provides DisabledRippleThemeColorAndAlpha) { Button { // ... } }
Devi modificare lo snippet riportato sopra per:
@OptIn(ExperimentalMaterialApi::class) private val MyRippleConfiguration = RippleConfiguration(color = Color.Red, rippleAlpha = MyRippleAlpha) // ... CompositionLocalProvider(LocalRippleConfiguration provides MyRippleConfiguration) { Button { // ... } }
Utilizzo di RippleTheme
per modificare a livello globale tutti gli echi in un'applicazione
In precedenza, potevi utilizzare LocalRippleTheme
per definire il comportamento dell'eco in un
a livello di tema. Si trattava essenzialmente di un punto di integrazione tra
di progettazione del sistema di produzione locali ed eco. Invece di esporre un generico
a tema, material-ripple
ora espone una createRippleModifierNode()
personalizzata. Questa funzione consente alle librerie del sistema di progettazione di creare
dell'implementazione wrapper
, che eseguono query sui valori del tema e poi delegano
l'implementazione di ripple nel nodo creato da questa funzione.
Ciò consente ai sistemi di progettazione di interrogare direttamente ciò di cui hanno bisogno ed esporre qualsiasi
richiesti livelli tematici configurabili dall'utente nella parte superiore senza doversi conformare
ciò che viene fornito al livello material-ripple
. Questa modifica aumenta
esplicitamente a quale tema/specifica si adatti l'eco, in quanto
sulla stessa API Ripple, che definisce il contratto, anziché essere implicitamente
derivati dal tema.
Per indicazioni, consulta l'implementazione dell'API Ripple in Material biblioteche e sostituire le richieste di assistenza ai canali di composizione Material secondo necessità il tuo sistema di progettazione.
Esegui la migrazione da Indication
a IndicationNodeFactory
Intorno a Indication
Se stai solo creando un Indication
da ignorare, ad esempio se stai creando un
onde per passare a Modifier.clickable
o Modifier.indication
,
devi apportare modifiche. IndicationNodeFactory
eredita da Indication
,
in modo che tutto continui a essere compilato e a funzionare.
Creazione di Indication
in corso...
Se stai creando la tua implementazione di Indication
, la migrazione dovrebbe
essere semplici nella maggior parte dei casi. Ad esempio, considera un valore Indication
che applica una
effetto scala alla pressione:
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() } } }
Puoi eseguire la migrazione in due passaggi:
Esegui la migrazione di
ScaleIndicationInstance
inDrawModifierNode
. La piattaforma API perDrawModifierNode
è molto simile aIndicationInstance
: espone una funzioneContentDrawScope#draw()
che è funzionalmente equivalente aIndicationInstance#drawContent()
. Devi modificare questa funzione implementare la logicacollectLatest
direttamente all'interno del nodo, anzichéIndication
.Ad esempio, il seguente snippet utilizza le API ritirate:
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() } } }
Devi modificare lo snippet riportato sopra per:
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() } } }
Esegui la migrazione di
ScaleIndication
per implementareIndicationNodeFactory
. Poiché la logica di raccolta dei dati è stata spostata nel nodo, la cui unica responsabilità è creare un'istanza del nodo.Ad esempio, il seguente snippet utilizza le API ritirate:
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 } }
Devi modificare lo snippet riportato sopra per:
object ScaleIndicationNodeFactory : IndicationNodeFactory { override fun create(interactionSource: InteractionSource): DelegatableNode { return ScaleIndicationNode(interactionSource) } override fun hashCode(): Int = -1 override fun equals(other: Any?) = other === this }
Utilizzo di Indication
per creare un IndicationInstance
Nella maggior parte dei casi, devi utilizzare Modifier.indication
per visualizzare Indication
per un
di strumento di authoring. Tuttavia, nel raro caso in cui crei manualmente un
IndicationInstance
utilizzando rememberUpdatedInstance
, devi aggiornare
implementazione per verificare se Indication
è un IndicationNodeFactory
, quindi
un'implementazione più semplice. Ad esempio, Modifier.indication
delega internamente al nodo creato se si tratta di un IndicationNodeFactory
. Se
non, utilizzerà Modifier.composed
per chiamare rememberUpdatedInstance
.