Sui dispositivi Wear OS, il rendering dei riquadri viene eseguito da due componenti chiave con versioni indipendenti. Per garantire che i riquadri della tua app funzionino correttamente su tutti i dispositivi, è importante comprendere questa architettura sottostante.
- Librerie Jetpack correlate ai riquadri: queste librerie (tra cui Wear Tiles
e Wear ProtoLayout) sono incorporate nella tua app e tu, in qualità di sviluppatore,
ne controlli le versioni. La tua app utilizza queste librerie per creare un
TileBuilder.Tileoggetto (la struttura di dati che rappresenta il riquadro) in risposta alla chiamataonTileRequest()del sistema. - Renderer ProtoLayout: questo componente di sistema è responsabile del rendering dell'oggetto
Tilesul display e della gestione delle interazioni dell'utente. La versione del renderer non è controllata dallo sviluppatore di app e può variare a seconda dei dispositivi, anche di quelli con hardware identico.
L'aspetto o il comportamento di un riquadro può variare in base alle versioni della libreria Jetpack Tiles della tua app e alla versione del renderer ProtoLayout sul dispositivo dell'utente. Ad esempio, un dispositivo potrebbe supportare la rotazione o la visualizzazione dei dati sulla frequenza cardiaca, mentre un altro potrebbe non supportarli.
Questo documento spiega come rendere la tua app compatibile con le diverse versioni della libreria Tiles e del renderer ProtoLayout. Spiega anche come eseguire la migrazione a versioni più recenti della libreria Jetpack.
Considerare la compatibilità
Per creare un riquadro che funzioni correttamente su una serie di dispositivi, valuta la possibilità di tenere conto del supporto variabile delle funzionalità. Puoi farlo tramite due strategie principali: rilevare le funzionalità del renderer in fase di runtime e fornire fallback integrati.
Rilevare le funzionalità del renderer
Puoi modificare dinamicamente il layout del riquadro in base alle funzionalità disponibili su un determinato dispositivo.
Rilevare la versione del renderer
- Utilizza il
getRendererSchemaVersion()metodo dell'DeviceParametersoggetto passato al tuoonTileRequest()metodo. Questo metodo restituisce i numeri di versione principale e secondaria del renderer ProtoLayout sul dispositivo. - Puoi quindi utilizzare la logica condizionale nell'implementazione di
onTileRequest()per adattare il design o il comportamento del riquadro in base alla versione del renderer rilevata.
L'annotazione @RequiresSchemaVersion
- L'annotazione
@RequiresSchemaVersionsui metodi ProtoLayout indica la versione minima dello schema del renderer richiesta per il comportamento documentato del metodo (esempio).- Anche se la chiamata di un metodo che richiede una versione del renderer più recente di quella disponibile sul dispositivo non causa l'arresto anomalo dell'app, potrebbe comportare la mancata visualizzazione dei contenuti o l'ignoranza della funzionalità.
Esempio di rilevamento della versione
val rendererVersion = requestParams.deviceConfiguration.rendererSchemaVersion val arcElement = // DashedArcLine has the annotation @RequiresSchemaVersion(major = 1, minor = 500) // and so is supported by renderer versions 1.500 and greater if ( rendererVersion.major > 1 || (rendererVersion.major == 1 && rendererVersion.minor >= 500) ) { // Use DashedArcLine if the renderer supports it … DashedArcLine.Builder() .setLength(degrees(270f)) .setThickness(8f) .setLinePattern( LayoutElementBuilders.DashedLinePattern.Builder() .setGapSize(8f) .setGapInterval(10f) .build() ) .build() } else { // … otherwise use ArcLine. ArcLine.Builder().setLength(degrees(270f)).setThickness(dp(8f)).build() }
Fornire fallback
Alcune risorse ti consentono di definire un fallback direttamente nel builder. Spesso è più semplice rispetto al controllo della versione del renderer ed è l'approccio preferito quando disponibile.
Un caso d'uso comune è fornire un'immagine statica come fallback per un'animazione Lottie. Se il dispositivo non supporta le animazioni Lottie, verrà eseguito il rendering dell'immagine statica.
val lottieImage = ResourceBuilders.ImageResource.Builder() .setAndroidLottieResourceByResId( ResourceBuilders.AndroidLottieResourceByResId.Builder(R.raw.lottie) .setStartTrigger(createOnVisibleTrigger()) .build() ) // Fallback if lottie is not supported .setAndroidResourceByResId( ResourceBuilders.AndroidImageResourceByResId.Builder() .setResourceId(R.drawable.lottie_fallback) .build() ) .build()
Testare con versioni diverse del renderer
Per testare i riquadri con versioni diverse del renderer, esegui il deployment su versioni diverse dell'emulatore Wear OS. (Sui dispositivi fisici, gli aggiornamenti del renderer ProtoLayout vengono forniti tramite il Play Store o gli aggiornamenti di sistema. Non è possibile forzare l'installazione di una versione specifica del renderer.)
La funzionalità di anteprima dei riquadri di Android Studio utilizza un renderer incorporato nella libreria Jetpack ProtoLayout da cui dipende il codice, quindi un altro approccio consiste nel dipendere da versioni diverse della libreria Jetpack durante il test dei riquadri.
Eseguire la migrazione a Tiles 1.5 / ProtoLayout 1.3 (Material 3 Expressive)
Aggiorna le librerie Jetpack Tiles per sfruttare i miglioramenti più recenti, incluse le modifiche dell'UI per integrare perfettamente i riquadri con il sistema.
Jetpack Tiles 1.5 e Jetpack ProtoLayout 1.3 introducono diversi miglioramenti e modifiche importanti. Tra cui:
- Un'API simile a Compose per descrivere l'UI.
- Componenti Material 3 Expressive, tra cui il pulsante sul bordo inferiore e il supporto per immagini migliorate: animazioni Lottie, più tipi di sfumature e nuovi stili di linee ad arco. - Nota: alcune di queste funzionalità possono essere utilizzate anche senza eseguire la migrazione a alla nuova API.
Consigli
Segui questi consigli quando esegui la migrazione dei riquadri:
- Esegui la migrazione di tutti i riquadri contemporaneamente. Evita di combinare le versioni dei riquadri all'interno dell'app. Anche se i componenti Material 3 si trovano in un artefatto separato (
androidx.wear.protolayout:protolayout-material3), il che rende tecnicamente possibile utilizzare sia i riquadri M2.5 che M3 nella stessa app, ti consigliamo vivamente di non adottare questo approccio a meno che non sia assolutamente necessario (ad esempio, se la tua app ha un numero elevato di riquadri che non possono essere migrati tutti contemporaneamente). - Adotta le linee guida per l'esperienza utente di Tiles. Data la natura altamente strutturata e basata su modelli dei riquadri, utilizza i design negli esempi esistenti come punti di partenza per i tuoi design.
- Esegui test su una varietà di dimensioni di schermo e caratteri. I riquadri sono spesso densi di informazioni, il che rende il testo (soprattutto se posizionato sui pulsanti) soggetto a overflow e ritaglio. Per ridurre al minimo questo problema, utilizza i componenti predefiniti ed evita personalizzazioni estese. Esegui i test utilizzando la funzionalità di anteprima dei riquadri di Android Studio'se su più dispositivi reali.
Processo di migrazione
Per eseguire la migrazione dei riquadri:
Aggiornare le dipendenze
Innanzitutto, aggiorna il file build.gradle.kts. Aggiorna le versioni e modifica la
protolayout-material dipendenza in protolayout-material3, come mostrato di seguito:
// In build.gradle.kts
//val tilesVersion = "1.4.1"
//val protoLayoutVersion = "1.2.1"
// Use these versions for M3.
val tilesVersion = "1.5.0"
val protoLayoutVersion = "1.3.0"
dependencies {
// Use to implement support for wear tiles
implementation("androidx.wear.tiles:tiles:$tilesVersion")
// Use to utilize standard components and layouts in your tiles
implementation("androidx.wear.protolayout:protolayout:$protoLayoutVersion")
// Use to utilize components and layouts with Material Design in your tiles
// implementation("androidx.wear.protolayout:protolayout-material:$protoLayoutVersion")
implementation("androidx.wear.protolayout:protolayout-material3:$protoLayoutVersion")
// Use to include dynamic expressions in your tiles
implementation("androidx.wear.protolayout:protolayout-expression:$protoLayoutVersion")
// Use to preview wear tiles in your own app
debugImplementation("androidx.wear.tiles:tiles-renderer:$tilesVersion")
// Use to fetch tiles from a tile provider in your tests
testImplementation("androidx.wear.tiles:tiles-testing:$tilesVersion")
}
TileService rimane in gran parte invariato
Le modifiche principali di questa migrazione riguardano i componenti dell'UI. Di conseguenza, l'implementazione di TileService, inclusi i meccanismi di caricamento delle risorse, dovrebbe richiedere modifiche minime o nulle.
L'eccezione principale riguarda il monitoraggio dell'attività dei riquadri: se la tua app utilizza
onTileEnterEvent() o onTileLeaveEvent(), ti consigliamo di
eseguire la migrazione a onRecentInteractionEventsAsync(). A partire dall'API 36, questi eventi verranno raggruppati.
Adattare il codice di generazione del layout
In ProtoLayout 1.2 (M2.5), il metodo onTileRequest() restituisce un
TileBuilders.Tile. Questo oggetto conteneva vari elementi, tra cui un
TimelineBuilders.Timeline, che a sua volta conteneva il LayoutElement
che descriveva l'UI del riquadro.
Con ProtoLayout 1.3 (M3), anche se la struttura e il flusso complessivi dei dati non sono cambiati, l'LayoutElement viene ora creato utilizzando un approccio ispirato a Compose con un layout basato su slot definiti che sono (dall'alto verso il basso) titleSlot (facoltativo; in genere per un titolo o un'intestazione principale), mainSlot (obbligatorio; per i contenuti principali) e bottomSlot (facoltativo; spesso per azioni come un pulsante sul bordo o informazioni supplementari come testo breve). Questo layout viene
creato dalla primaryLayout() funzione.
Confronto tra le funzioni di layout M2.5 e M3
M2.5
fun myLayout( context: Context, deviceConfiguration: DeviceParametersBuilders.DeviceParameters ) = PrimaryLayout.Builder(deviceConfiguration) .setResponsiveContentInsetEnabled(true) .setContent( Text.Builder(context, "Hello World!") .setTypography(Typography.TYPOGRAPHY_BODY1) .build() ) .build()
M3
fun myLayout( context: Context, deviceConfiguration: DeviceParametersBuilders.DeviceParameters, ) = materialScope(context, deviceConfiguration) { primaryLayout(mainSlot = { text("Hello, World!".layoutString) }) }
Per evidenziare le differenze principali:
- Eliminazione dei builder. Il precedente pattern di builder per i componenti dell'UI Material è stato sostituito da una sintassi più dichiarativa e ispirata a Compose. Anche i componenti non UI, come String/Color/Modifiers, ricevono nuovi wrapper Kotlin.
- Funzioni di inizializzazione e layout standardizzate. I layout M3 si basano su
funzioni di inizializzazione e struttura standardizzate:
materialScope()eprimaryLayout(). Queste funzioni obbligatorie inizializzano l'ambiente M3 (applicazione di temi, ambito dei componenti utilizzandomaterialScope) e definiscono il layout principale basato su slot (utilizzandoprimaryLayout). Entrambe devono essere chiamate esattamente una volta per layout.
Applicazione tema
Material 3 introduce diverse modifiche all'applicazione di temi, tra cui il colore dinamico e un set ampliato di opzioni di tipografia e forma.
Colore
Una funzionalità di spicco di Material 3 Expressive è l'"applicazione di temi dinamici": i riquadri che attivano questa funzionalità (attivata per impostazione predefinita) verranno visualizzati nel tema fornito dal sistema (la disponibilità dipende dal dispositivo e dalla configurazione dell'utente).
Un'altra modifica in M3 è l'espansione del numero di token di colore, che è aumentato da 4 a 29. I nuovi token di colore sono disponibili nella
ColorScheme classe.
Tipografia
Analogamente a M2.5, M3 si basa fortemente su costanti di dimensioni dei caratteri predefinite. È sconsigliato specificare direttamente le dimensioni dei caratteri. Queste costanti si trovano nella
Typography classe e offrono una gamma leggermente più ampia di opzioni più espressive.
Per tutti i dettagli, consulta la documentazione sulla tipografia.
Forma
La maggior parte dei componenti M3 può variare in base alla dimensione della forma e del colore.
Un textButton (in the mainSlot) con forma full:
Lo stesso textButton con forma small:
Componenti
I componenti M3 sono più flessibili e configurabili rispetto alle loro controparti M2.5. M2.5 spesso richiedeva componenti distinti per trattamenti visivi diversi, mentre M3 utilizza spesso un componente di base generalizzato e altamente configurabile con valori predefiniti validi.
Questo principio si applica anche al layout principale. In M2.5, si trattava di un
PrimaryLayout o di un EdgeContentLayout. In M3, dopo aver
stabilito un singolo MaterialScope di primo livello, chiama la
primaryLayout() funzione. Questa funzione restituisce direttamente il layout principale, senza bisogno di builder, e accetta LayoutElements per diversi slot, come titleSlot, mainSlot e bottomSlot. Puoi popolare questi slot con
componenti dell'UI concreti, come quelli restituiti da text(),
button() o card(), o con strutture di layout, come Row o
Column da LayoutElementBuilders.
I temi sono un altro miglioramento chiave di M3. Per impostazione predefinita, gli elementi dell'UI rispettano automaticamente le specifiche di stile M3 e supportano l'applicazione di temi dinamici.
| M2.5 | M3 |
|---|---|
| Elementi interattivi | |
Button o Chip |
|
| Testo | |
Text |
text() |
| Indicatori di avanzamento | |
CircularProgressIndicator |
circularProgressIndicator() o segmentedCircularProgressIndicator() |
| Layout | |
PrimaryLayout o EdgeContentLayout |
primaryLayout() |
| — | buttonGroup() |
| Google Immagini | |
| — | icon(), avatarImage() o backgroundImage() |
Modificatori
In M3, Modifiers, che utilizzi per decorare o aumentare un componente, sono
più simili a Compose. Questa modifica può ridurre il codice boilerplate creando automaticamente i tipi interni appropriati. Questa modifica è ortogonale all'utilizzo dei componenti dell'UI M3; se necessario, puoi utilizzare i modificatori in stile builder di ProtoLayout 1.2 con i componenti dell'UI M3 e viceversa.
M2.5
// Uses Builder-style modifier to set opacity fun myModifier(): ModifiersBuilders.Modifiers = ModifiersBuilders.Modifiers.Builder() .setOpacity(TypeBuilders.FloatProp.Builder(0.5F).build()) .build()
M3
// Uses Compose-like modifiers to set opacity fun myModifier(): LayoutModifier = LayoutModifier.opacity(0.5F)
Puoi creare modificatori utilizzando uno dei due stili API e puoi anche utilizzare la
toProtoLayoutModifiers() funzione di estensione per convertire un
LayoutModifier in un ModifiersBuilders.Modifier.
Funzioni helper
Anche se ProtoLayout 1.3 consente di esprimere molti componenti dell'UI utilizzando un'
API ispirata a Compose, gli elementi di layout fondamentali come righe e
colonne da LayoutElementBuilders continuano a utilizzare il pattern di builder. Per colmare questa lacuna stilistica e promuovere la coerenza con le nuove API dei componenti M3, valuta la possibilità di utilizzare le funzioni helper.
Senza helper
primaryLayout( mainSlot = { Column.Builder() .setWidth(expand()) .setHeight(expand()) .addContent(text("A".layoutString)) .addContent(text("B".layoutString)) .addContent(text("C".layoutString)) .build() } )
Con gli helper
// Function literal with receiver helper function fun column(builder: Column.Builder.() -> Unit) = Column.Builder().apply(builder).build() primaryLayout( mainSlot = { column { setWidth(expand()) setHeight(expand()) addContent(text("A".layoutString)) addContent(text("B".layoutString)) addContent(text("C".layoutString)) } } )
Eseguire la migrazione a Tiles 1.2 / ProtoLayout 1.0
A partire dalla versione 1.2, la maggior parte delle API di layout di Tiles si trova nello spazio dei nomi androidx.wear.protolayout. Per utilizzare le API più recenti, completa i seguenti passaggi di migrazione nel codice.
Aggiornare le dipendenze
Nel file di build del modulo dell'app, apporta le seguenti modifiche:
Alla moda
// Removeimplementation 'androidx.wear.tiles:tiles-material:version'// Include additional dependencies implementation "androidx.wear.protolayout:protolayout:1.4.0" implementation "androidx.wear.protolayout:protolayout-material:1.4.0" implementation "androidx.wear.protolayout:protolayout-expression:1.4.0" // Update implementation "androidx.wear.tiles:tiles:1.6.0"
Kotlin
// Removeimplementation("androidx.wear.tiles:tiles-material:version")// Include additional dependencies implementation("androidx.wear.protolayout:protolayout:1.4.0") implementation("androidx.wear.protolayout:protolayout-material:1.4.0") implementation("androidx.wear.protolayout:protolayout-expression:1.4.0") // Update implementation("androidx.wear.tiles:tiles:1.6.0")
Aggiornare gli spazi dei nomi
Nei file di codice basati su Kotlin e Java dell'app, apporta i seguenti aggiornamenti: In alternativa, puoi eseguire questo script di ridenominazione dello spazio dei nomi.
- Sostituisci tutti gli import
androidx.wear.tiles.material.*conandroidx.wear.protolayout.material.*. Completa questo passaggio anche per la libreriaandroidx.wear.tiles.material.layouts. Sostituisci la maggior parte degli altri import
androidx.wear.tiles.*conandroidx.wear.protolayout.*.Gli import per
androidx.wear.tiles.EventBuilders,androidx.wear.tiles.RequestBuilders,androidx.wear.tiles.TileBuilderseandroidx.wear.tiles.TileServicedevono rimanere invariati.Rinomina alcuni metodi ritirati delle classi TileService e TileBuilder:
TileBuilders:getTimeline()ingetTileTimeline()esetTimeline()insetTileTimeline()TileService:onResourcesRequest()inonTileResourcesRequest()RequestBuilders.TileRequest:getDeviceParameters()ingetDeviceConfiguration(),setDeviceParameters()insetDeviceConfiguration(),getState()ingetCurrentState()esetState()insetCurrentState()