Software Engineer
In questa sessione, approfondiremo gli aspetti tecnici dell'evoluzione dalle schede a schermo intero ai widget Wear dinamici ad altezza parziale.
Esploreremo i vantaggi tecnici dell'API Jetpack Glance unificata e del nuovo motore Remote Compose, spiegando in dettaglio come si allineano perfettamente con Modern Android Development.
Infine, condivideremo le ultime linee guida sulla UX e illustreremo strategie di migrazione pratiche per aiutarti a creare la prossima generazione di UI facilmente consultabili.
Trascrizione
Ciao a tutti.
Mi chiamo Frank e sono responsabile delle esperienze rapide per Wear OS.
Gli smartwatch sono compagni essenziali per i tuoi utenti, che li indossano tutto il giorno. Oggi ti spiegheremo come sfruttare la nuova esperienza dei widget di Wear.
Vedremo come fornire le funzionalità più importanti direttamente al polso degli utenti e lo faremo utilizzando le API più recenti, progettate per rendere la creazione di app per Wear OS familiare e intuitiva per gli sviluppatori Android come te.
Oggi sul palco con me per esaminare esattamente come lo stiamo facendo c'è il mio brillante collega e compagno di scrivania Yuri.
Ciao, sono Yuri Schimke.
Sono un ingegnere del team di Wear OS e mi occupo principalmente delle API Remote Compose.
Oggi ti presenterò brevemente Remote Compose e ti parlerò anche della procedura di migrazione da Tiles a Wear Widgets.
A questo punto, ripasso la parola a Frank.
Fantastico.
Grazie, Yuri.
Stiamo ufficialmente portando il polso nell'ecosistema unificato dei widget Android, trasformando le nostre schede a schermo intero in widget Wear.
E poiché queste esperienze sono ora create utilizzando Jetpack Glance, puoi vedere che si tratta della stessa infrastruttura di base utilizzata per i widget mobile.
In futuro, lo sviluppo per Wear diventerà ancora più familiare per te.
Per supportare queste esperienze, stiamo evolvendo lo stack UI, lo stack UI sottostante.
I widget per Wear sono basati su Remote Compose, un nuovo framework Android per l'UI remota.
Ciò significa che puoi finalmente utilizzare un'API dichiarativa strettamente allineata a Jetpack Compose per creare le tue esperienze esterne all'app, come i widget.
Remote Compose offre grafica moderna e layout adattabili ai widget, garantendo al contempo prestazioni ed efficienza della batteria.
Inoltre, disaccoppiando la UI dal processo dell'applicazione, i widget vengono serializzati in modo sicuro e visualizzati facilmente su un'ampia gamma di dispositivi Android.
Ciò significa che puoi portare queste esperienze complete su quasi tutte le piattaforme remote supportate con il minimo sforzo.
Per rendere la transizione dalle schede ai widget il più agevole possibile, stiamo introducendo due dimensioni standard per i contenitori: piccola e grande.
Questi sono progettati per essere strettamente allineati ai formati di widget basati su griglia 2x1 e 2x2 a cui sei già abituato con la creazione di widget su dispositivi mobili.
Inoltre, a partire da Wear 7 su alcuni produttori di hardware, i widget di Wear saranno ora disponibili in una barra a scorrimento verticale insieme ai widget di altre app.
Ad esempio, ecco come appaiono i widget Wear su un Samsung Galaxy Watch.
L'architettura della piattaforma è progettata per supportare i carrelli a scorrimento verticale, poiché i produttori di hardware adotteranno questo modello in futuro.
Inoltre, non devi preoccuparti di lasciare indietro utenti o utenti esistenti.
Le API Jetpack Glance Wear sono progettate per supportare la compatibilità con le versioni precedenti fino a Wear 4.
Sui dispositivi meno recenti che supportano una barra orizzontale di widget o di riquadri, il sistema gestirà questa operazione per te.
Devi semplicemente indicare nel codice che vuoi supportare la modalità a schermo intero e il sistema prenderà il tuo widget di grandi dimensioni e lo mostrerà come una scheda tradizionale a schermo intero su questi dispositivi.
Ora Yuri ci guiderà attraverso il codice per mostrarci esattamente come eseguire la migrazione delle schede esistenti in widget Wear.
Grazie, Frank.
Se stai creando un nuovo widget per Wear da zero, è semplice.
Devi solo definire un GlanceWearWidgetService e creare la tua UI utilizzando Remote Compose.
Tuttavia, se la tua app ha già una scheda in produzione, dovrai eseguire la migrazione di servizi, UI e dati.
Per rendere questa transizione il più agevole possibile, abbiamo fornito percorsi per garantire un'esperienza senza interruzioni per gli utenti esistenti.
Per il servizio, puoi scegliere tra due strategie: servizio doppio o servizio singolo.
Con il doppio servizio, che consigliamo, manterrai il TileService esistente per i dispositivi Wear OS meno recenti e aggiungerai un nuovo GlanceWearWidgetService per Wear OS 7 e versioni successive.
Al contrario, con il servizio singolo, crei un nuovo GlanceWearWidgetService singolo che gestisce entrambi i protocolli, in esecuzione in una modalità di compatibilità per tradurre il widget in un riquadro sui dispositivi meno recenti.
In entrambi i casi, continuerai a raggiungere i tuoi utenti.
Influisce solo sul codice che gestirai in futuro.
Abbiamo appena pubblicato una documentazione completa sul sito per sviluppatori Android che spiega esattamente come e quando utilizzare entrambi questi pattern.
Dai un'occhiata per scoprire di più sulla strategia migliore per la tua app.
Ora parliamo del livello UI.
Durante la migrazione, il tuo modello mentale cambia.
In precedenza, i riquadri utilizzavano ProtoLayout, una libreria creata esclusivamente per i riquadri di Wear.
Wear Widgets, invece, si basa su Remote Compose.
Remote Compose condivide esattamente la stessa base della UI di Jetpack Compose, utilizzando lo stesso compilatore e runtime di base.
Inoltre, è progettato per essere familiare, in quanto corrisponde strettamente alle librerie di UI di Compose come Foundation e Material 3.
Ma invece di una casella, una colonna o un testo, li scambierai semplicemente con RemoteBox, RemoteColumn o RemoteText.
In questo caso, acquisirai familiarità con la scrittura di "remote".
Inoltre, per velocizzare il tuo flusso di lavoro, abbiamo incluso un set completo di componenti Remote Compose pronti all'uso progettati specificamente per i widget.
Ciò significa che i tuoi progetti Wear Material 3 esistenti, inclusi quelli che hai in Figma, possono essere facilmente adattati ai widget, rendendoli familiari sia agli ingegneri che ai designer.
Per capire quanto siano allineati questi framework, diamo un'occhiata a un esempio concreto.
Creeremo il nostro componente dell'app sopra il pulsante di attivazione/disattivazione di Material 3.
A sinistra abbiamo il componente Wear Compose standard che scriveresti all'interno della tua app, mentre a destra è presente la versione di Remote Compose.
// Wear Compose
@Composable
fun HistoryEraserButton(
onClick: () -> Unit,
modifier: Modifier = Modifier,
checked: Boolean = true,
onCheckedChange: (Boolean) -> Unit = {},
)// Wear RemoteCompose
@RemoteComposable
@Composable
fun RemoteHistoryEraserButton(
onClick: Action,
modifier: RemoteModifier = RemoteModifier,
checked: RemoteBoolean = true.rb,
onCheckedChange: Action = Action.Empty,
)Nota quanto sono simili i pattern.
Tuttavia, quando scrivi il codice di Remote Compose, utilizzerai l'annotazione RemoteComposable.
E dove utilizzi parametri familiari come modificatore e booleano, esistono tipi di Remote Compose equivalenti come RemoteModifier e RemoteBoolean.
Inoltre, utilizzi le azioni anziché le lambda.
Anziché definire il codice da eseguire all'interno della funzione lambda, dovrai dichiarare un'azione, ad esempio l'avvio di un pending intent, la chiamata di un servizio o l'aggiornamento di un valore.
Passiamo al corpo del nostro componente.
Noterai che anche i temi sono allineati.
Puoi applicare la combinazione di colori di Material 3 al componente Remote Compose esattamente come faresti in Compose standard, in modo che il widget corrisponda esattamente al branding della tua app.
// Wear Compose
val colors = MaterialTheme.colorScheme
SplitSwitchButton(
checked = checked,
onCheckedChange = onCheckedChange,
toggleContentDescription = "Arm Eraser",
onContainerClick = onClick,
modifier = modifier.fillMaxWidth(),
colors =
SwitchButtonDefaults.splitSwitchButtonColors(
checkedContainerColor = colors.errorContainer, …
),
label = { Text("History Eraser") },
secondaryLabel = { Text("Don't touch it!") },
)// Wear RemoteCompose
val colors = RemoteMaterialTheme.colorScheme
RemoteSplitSwitchButton(
checked = checked,
onCheckedChange = onCheckedChange,
toggleContentDescription = "Arm Eraser".rs,
onContainerClick = onClick,
modifier = modifier.fillMaxWidth(),
colors =
RemoteSplitSwitchButtonDefaults.splitSwitchButtonColors(
checkedContainerColor = colors.errorContainer, …
),
label = { RemoteText("History Eraser".rs) },
secondaryLabel = { RemoteText("Don't touch it!".rs) },
)Infine, vediamo il risultato. L'interfaccia utente risultante è esattamente quella che ti aspetteresti: un pulsante di attivazione/disattivazione pulito, reattivo e animato.
È difficile distinguere la differenza.
Per aiutarti a progettare, ripetere e verificare i layout dei widget senza doverli sempre eseguire su un dispositivo o un emulatore, abbiamo aggiunto il supporto dell'anteprima di Compose per i widget Wear.
Poiché i widget dipendono da vincoli del container specifici dell'OEM, come dimensioni, forma, spaziatura interna e raggio degli angoli, ci basiamo sull'annotazione di anteprima standard, ma con un provider di parametri aggiuntivo per inserire queste configurazioni.
@Preview(name = "Squircle Preview", device = "spec:width=1000dp,height=1000dp,dpi=320")
@Composable
fun WeatherWidgetSquirclePreview(
@PreviewParameter(SquircleAllWidgetPreviewParams::class) params: WearWidgetParams
) = WearWidgetPreview(MockWeatherWidget(65, WeatherCondition.CLOUDY), params)
@Preview(name = "Round Preview", device = "spec:width=1000dp,height=1000dp,dpi=320")
@Composable
fun WeatherWidgetRoundPreview(
@PreviewParameter(RoundAllWidgetPreviewParams::class) params: WearWidgetParams
) = WearWidgetPreview(MockWeatherWidget(65, WeatherCondition.CLOUDY), params)La libreria fornisce implementazioni del provider PreviewParameter preconfigurate che corrispondono ai dispositivi Wear OS reali.
Queste annotazioni di anteprima ti consentono di verificare facilmente il layout su diverse forme di hardware.
Oltre a testare i layout, puoi utilizzare queste anteprime per generare le immagini statiche richieste per il selettore di widget sul dispositivo.
@Preview(name = "Widget Picker Preview", device = "spec:width=1000dp,height=1000dp,dpi=320")
@Composable
fun WeatherWidgetRectangularPreview(
@PreviewParameter(RectangularAllWidgetPreviewParams::class) params: WearWidgetParams
) = WearWidgetPreview(MockWeatherWidget(65, WeatherCondition.CLOUDY), params)La libreria fornisce un parametro per la forma di anteprima rettangolare che emula i contenitori rettangolari completi non ritagliati.
Se vuoi vedere esattamente come funziona in pratica, abbiamo aggiornato la nostra app di esempio multimediale Horologist per includere un nuovo widget che mostra questa API.
Mostriamo come aggiungere sia la versione piccola sia quella grande del widget multimediale.
Dai un'occhiata su GitHub per vedere il confronto diretto tra il codice ProtoLayout precedente e la nuova implementazione di Remote Compose.
Siamo certi che ti piacerà.
Infine, non sarebbe un discorso completo senza menzionare l'AI.
Per velocizzare la migrazione, abbiamo pubblicato su GitHub una nuova skill per la migrazione da riquadri a widget.
Puoi utilizzarlo con l'agente che preferisci o aggiungerlo direttamente ad Android Studio utilizzando la CLI Android.
android skills add wear-widgets
android skills add --allOra passo la parola a François per concludere.
Grazie.
Grazie, Yuri.
Come puoi vedere, trasformare le tue schede in widget Wear è un processo molto semplice.
In sintesi, eseguendo la migrazione a Jetpack Glance Wear e Remote Compose, non stai semplicemente aggiornando la tua UI, ma stai entrando a far parte di un ecosistema di widget Android più unificato.
Ora puoi creare widget accattivanti e compatibili con le versioni precedenti utilizzando un framework moderno e dichiarativo completamente allineato a Jetpack Compose.
Puoi iniziare a eseguire la migrazione dei tuoi riquadri oggi stesso consultando la guida introduttiva sul sito per sviluppatori Android per tutti i dettagli.
Non vediamo l'ora di vedere tutte le esperienze espressive e di facile lettura che creerai per gli smartwatch.
Grazie mille per aver partecipato oggi, questa mattina, e ci auguriamo che il resto della conferenza sia di tuo gradimento.
Grazie.
// Wear Compose
@Composable
fun HistoryEraserButton(
onClick: () -> Unit,
modifier: Modifier = Modifier,
checked: Boolean = true,
onCheckedChange: (Boolean) -> Unit = {},
)// Wear RemoteCompose
@RemoteComposable
@Composable
fun RemoteHistoryEraserButton(
onClick: Action,
modifier: RemoteModifier = RemoteModifier,
checked: RemoteBoolean = true.rb,
onCheckedChange: Action = Action.Empty,
)// Wear Compose
val colors = MaterialTheme.colorScheme
SplitSwitchButton(
checked = checked,
onCheckedChange = onCheckedChange,
toggleContentDescription = "Arm Eraser",
onContainerClick = onClick,
modifier = modifier.fillMaxWidth(),
colors =
SwitchButtonDefaults.splitSwitchButtonColors(
checkedContainerColor = colors.errorContainer, …
),
label = { Text("History Eraser") },
secondaryLabel = { Text("Don't touch it!") },
)// Wear RemoteCompose
val colors = RemoteMaterialTheme.colorScheme
RemoteSplitSwitchButton(
checked = checked,
onCheckedChange = onCheckedChange,
toggleContentDescription = "Arm Eraser".rs,
onContainerClick = onClick,
modifier = modifier.fillMaxWidth(),
colors =
RemoteSplitSwitchButtonDefaults.splitSwitchButtonColors(
checkedContainerColor = colors.errorContainer, …
),
label = { RemoteText("History Eraser".rs) },
secondaryLabel = { RemoteText("Don't touch it!".rs) },
)@Preview(name = "Squircle Preview", device = "spec:width=1000dp,height=1000dp,dpi=320")
@Composable
fun WeatherWidgetSquirclePreview(
@PreviewParameter(SquircleAllWidgetPreviewParams::class) params: WearWidgetParams
) = WearWidgetPreview(MockWeatherWidget(65, WeatherCondition.CLOUDY), params)
@Preview(name = "Round Preview", device = "spec:width=1000dp,height=1000dp,dpi=320")
@Composable
fun WeatherWidgetRoundPreview(
@PreviewParameter(RoundAllWidgetPreviewParams::class) params: WearWidgetParams
) = WearWidgetPreview(MockWeatherWidget(65, WeatherCondition.CLOUDY), params)@Preview(name = "Widget Picker Preview", device = "spec:width=1000dp,height=1000dp,dpi=320")
@Composable
fun WeatherWidgetRectangularPreview(
@PreviewParameter(RectangularAllWidgetPreviewParams::class) params: WearWidgetParams
) = WearWidgetPreview(MockWeatherWidget(65, WeatherCondition.CLOUDY), params)android skills add wear-widgets
android skills add --all