Per iniziare a fornire riquadri dalla tua app, includi le seguenti dipendenze nel file build.gradle
della tua app.
Alla moda
dependencies { // Use to implement support for wear tiles implementation "androidx.wear.tiles:tiles:1.4.1" // Use to utilize standard components and layouts in your tiles implementation "androidx.wear.protolayout:protolayout:1.2.1" // Use to utilize components and layouts with Material Design in your tiles implementation "androidx.wear.protolayout:protolayout-material:1.2.1" // Use to include dynamic expressions in your tiles implementation "androidx.wear.protolayout:protolayout-expression:1.2.1" // Use to preview wear tiles in your own app debugImplementation "androidx.wear.tiles:tiles-renderer:1.4.1" // Use to fetch tiles from a tile provider in your tests testImplementation "androidx.wear.tiles:tiles-testing:1.4.1" }
Kotlin
dependencies { // Use to implement support for wear tiles implementation("androidx.wear.tiles:tiles:1.4.1") // Use to utilize standard components and layouts in your tiles implementation("androidx.wear.protolayout:protolayout:1.2.1") // Use to utilize components and layouts with Material Design in your tiles implementation("androidx.wear.protolayout:protolayout-material:1.2.1") // Use to include dynamic expressions in your tiles implementation("androidx.wear.protolayout:protolayout-expression:1.2.1") // Use to preview wear tiles in your own app debugImplementation("androidx.wear.tiles:tiles-renderer:1.4.1") // Use to fetch tiles from a tile provider in your tests testImplementation("androidx.wear.tiles:tiles-testing:1.4.1") }
Concetti principali
Le schede non sono create nello stesso modo delle app per Android e utilizzano diversi concetti:
- Modelli di layout:definiscono la disposizione complessiva degli elementi visivi sul display. Questo viene ottenuto tramite la funzione
primaryLayout()
. - Elementi di layout:rappresentano un singolo elemento grafico, ad esempio un pulsante o una scheda, oppure più elementi raggruppati utilizzando una colonna, un buttonGroup o un elemento simile. Questi elementi sono incorporati in un modello di layout.
- Risorse: gli oggetti
ResourceBuilders.Resources
sono costituiti da una mappa di coppie chiave-valore delle risorse Android (immagini) necessarie per eseguire il rendering di un layout e da una versione. - Sequenza temporale:un oggetto
TimelineBuilders.Timeline
è un elenco di una o più istanze di un oggetto layout. Puoi fornire vari meccanismi ed espressioni per indicare quando il visualizzatore deve passare da un oggetto layout a un altro, ad esempio per interrompere la visualizzazione di un layout in un momento specifico. - Stato: una struttura di dati di tipo
StateBuilders.State
che viene passata tra riquadro e app per consentire ai due componenti di comunicare tra loro. Ad esempio, se viene toccato un pulsante nel riquadro, lo stato contiene l'ID del pulsante. Puoi anche scambiare i tipi di dati utilizzando una mappa. - Riquadro:un oggetto
TileBuilders.Tile
che rappresenta un riquadro, costituito da una sequenza temporale, un ID versione delle risorse, un intervallo di aggiornamento e uno stato. - Protolayout: questo termine compare nel nome di varie classi relative ai riquadri e si riferisce alla libreria Protolayout per Wear OS, una libreria di grafica utilizzata su varie piattaforme Wear OS.
Creare un riquadro
Per fornire un riquadro dalla tua app, implementa un servizio di tipo TileService
e registralo nel file manifest. Da qui, il sistema richiede i riquadri necessari durante le chiamate a onTileRequest()
e le risorse durante le chiamate a
onTileResourcesRequest()
.
class MyTileService : TileService() { override fun onTileRequest(requestParams: RequestBuilders.TileRequest) = Futures.immediateFuture( Tile.Builder() .setResourcesVersion(RESOURCES_VERSION) .setTileTimeline( Timeline.fromLayoutElement( materialScope(this, requestParams.deviceConfiguration) { primaryLayout( mainSlot = { text("Hello, World!".layoutString, typography = BODY_LARGE) } ) } ) ) .build() ) override fun onTileResourcesRequest(requestParams: ResourcesRequest) = Futures.immediateFuture( Resources.Builder().setVersion(RESOURCES_VERSION).build() ) }
Aggiungi un servizio all'interno del tag <application>
del file AndroidManifest.xml
.
<service android:name=".snippets.m3.tile.MyTileService" android:label="@string/tile_label" android:description="@string/tile_description" android:icon="@mipmap/ic_launcher" android:exported="true" android:permission="com.google.android.wearable.permission.BIND_TILE_PROVIDER"> <intent-filter> <action android:name="androidx.wear.tiles.action.BIND_TILE_PROVIDER" /> </intent-filter> <meta-data android:name="androidx.wear.tiles.PREVIEW" android:resource="@drawable/tile_preview" /> </service>
Il filtro delle autorizzazioni e delle intenzioni registra questo servizio come fornitore di riquadri.
Le risorse icona, etichetta, descrizione e anteprima vengono mostrate all'utente quando configura i riquadri sullo smartphone o sullo smartwatch. Tieni presente che la risorsa di anteprima supporta tutti i qualificatori delle risorse standard di Android, quindi è possibile variare l'anteprima in base a fattori come le dimensioni dello schermo e la lingua del dispositivo. Per altri consigli, consulta la lista di controllo di anteprima.
Esegui il deployment dell'app e aggiungi il riquadro al carosello di riquadri (esiste anche un modo più adatto agli sviluppatori per visualizzare l'anteprima di un riquadro, ma per il momento esegui l'operazione manualmente).

Per un esempio completo, consulta il codice di esempio su GitHub o il codelab.
Creare l'interfaccia utente per le riquadri
Gli elementi dell'interfaccia utente espressiva di Material 3 vengono creati utilizzando un approccio strutturato basato sul pattern di builder sicuro per i tipi di Kotlin.
Layout
Per creare il layout:
Avvia un ambito Material Design: chiama la funzione
materialScope()
, fornendocontext
edeviceConfiguration
richiesti. Puoi includere parametri facoltativi, comeallowDynamicTheme
edefaultColorScheme
. Per impostazione predefinita,allowDynamicTheme
ètrue
edefaultColorScheme
rappresenta ilColorScheme
utilizzato quando i colori dinamici non sono disponibili, ad esempio quando l'utente ha disattivato la funzionalità, o quando non è supportata dal dispositivo oallowDynamicTheme
èfalse
.Crea l'interfaccia utente nell'ambito: tutti i componenti dell'interfaccia utente per un determinato layout dei riquadri devono essere definiti all'interno della funzione lambda di una singola chiamata di materialScope() di primo livello. Queste funzioni di componenti, come
primaryLayout()
etextEdgeButton()
, sono funzioni di estensione diMaterialScope
e sono disponibili solo quando vengono chiamate in questo ambito del ricevitore.materialScope( context = context, deviceConfiguration = requestParams.deviceConfiguration, // requestParams is passed to onTileRequest defaultColorScheme = myFallbackColorScheme ) { // inside the MaterialScope, you can call functions like primaryLayout() primaryLayout( titleSlot = { text(text = "Title".layoutString) }, mainSlot = { text(text = "Main Content".layoutString) }, bottomSlot = { textEdgeButton(text = "Action".layoutString) } ) }
Slot machine
In M3, il layout delle schede utilizza un approccio ispirato a Compose che sfrutta tre slot distinti. Dall'alto verso il basso, sono:
titleSlot
, in genere per un titolo o un'intestazione principale.mainSlot
, per i contenuti principali.bottomSlot
, spesso utilizzato per azioni o informazioni supplementari. Qui viene visualizzato anche un pulsante laterale.

I contenuti di ogni spazio sono i seguenti:
titleSlot
(facoltativo): in genere alcune parole generate datext()
.mainSlot
(obbligatorio): componenti organizzati in strutture come righe, colonne e gruppi di pulsanti. Questi componenti possono anche essere incorporati in modo ricorsivo l'uno nell'altro; ad esempio, una colonna può contenere righe.bottomSlot
(facoltativo): in genere viene inserito un pulsante adiacente al bordo o un'etichetta di testo.
Poiché non è possibile scorrere i riquadri, non sono disponibili componenti per la visualizzazione di pagine, lo scorrimento o la gestione di elenchi di contenuti lunghi. Assicurati che i contenuti rimangano visibili quando le dimensioni dei caratteri aumentano o il testo diventa più lungo a causa della traduzione.
Componenti dell'interfaccia utente
La libreria protolayout-material3
fornisce un numero elevato di componenti progettati in base alle specifiche di Material 3 Expressive e ai consigli per l'interfaccia utente.
Pulsanti
- textButton(): pulsante con un singolo slot per contenuti di testo (brevi)
- iconButton(): pulsante con un singolo slot per rappresentare un'icona
- avatarButton(): pulsante avatar a forma di pillola che offre fino a tre scomparti per inserire contenuti che rappresentano un'etichetta e un'etichetta secondaria impilate verticalmente e un'immagine (avatar) accanto
- imageButton(): pulsante immagine selezionabile che non offre altri scomparti, solo immagine (ad esempio backgroundImage come sfondo)
- compactButton(): pulsante compatto che offre fino a due slot per inserire contenuti impilzati orizzontalmente che rappresentano un'icona e un testo accanto
- button(): pulsante a forma di pillola che offre fino a tre spazi per inserire contenuti che rappresentano un'etichetta e un'etichetta secondaria impilate verticalmente e un'icona accanto
Pulsanti laterali
- iconEdgeButton(): pulsante laterale che offre un singolo slot per inserire un'icona o contenuti piccoli e rotondi simili
- textEdgeButton(): pulsante laterale che offre un singolo spazio per inserire un testo o contenuti di dimensioni simili
Carte
- titleCard(): scheda del titolo che offre da uno a tre spazi, solitamente basati su testo
- appCard(): scheda dell'app che offre fino a cinque slot, in genere basati su testo
- textDataCard(): scheda di dati che offre fino a tre slot impilati verticalmente, in genere basati su testo o numeri
- iconDataCard(): scheda di dati che offre fino a tre slot impilati verticalmente, in genere basati su testo o numeri, con un'icona
- graphicDataCard(): scheda di dati grafici che offre uno slot per i dati grafici, ad esempio l'indicatore di avanzamento, e fino a due slot impilati verticalmente, solitamente per le descrizioni di testo
Indicatori di avanzamento
- circularProgressIndicator(): indica l'avanzamento verso un obiettivo utilizzando un elemento radiale
- segmentedCircularProgressIndicator(): indica i progressi verso un obiettivo utilizzando un elemento radiale con fasi distinte
Raggruppare elementi di layout
- buttonGroup(): componente di layout che inserisce gli elementi secondari in una sequenza orizzontale
- primaryLayout(): layout a schermo intero che rappresenta uno stile di layout M3 suggerito, responsive e che si occupa del posizionamento degli elementi, insieme ai margini e ai padding consigliati applicati
Applicazione tema
In Material 3 Expressive, il sistema di colori è definito da 29 ruoli di colore standard, organizzati in sei gruppi: primario, secondario, terziario, errore, superficie e contorni.

Un ColorScheme
mappa ciascuno di questi 29 ruoli a un colore corrispondente e, poiché fa parte del MaterialScope
e i componenti devono essere creati al suo interno, acquisiscono automaticamente i colori dallo schema. Questo approccio consente a tutti gli elementi dell'interfaccia utente di rispettare automaticamente gli standard di Material Design.
Per consentire agli utenti di scegliere tra una combinazione di colori definita da te, ad esempio una che rispecchi i colori del tuo brand, e una fornita dal sistema, ricavata dal quadrante corrente dell'utente o scelta dall'utente, inizializza MaterialScope
come segue:
val myColorScheme =
ColorScheme(
primary = ...
onPrimary = ...
// 27 more
)
materialScope(
defaultColorScheme = myColorScheme
) {
// If the user selects "no theme" in settings, myColorScheme is used.
// Otherwise, the system-provided theme is used.
}
Per forzare la visualizzazione dei riquadri nella combinazione di colori che fornisci, disattiva il supporto per i temi dinamici impostando allowDynamicTheme
su false
:
materialScope(
allowDynamicTheme = false,
defaultColorScheme = myColorScheme
) {
// myColorScheme is *always* used.
}
Colore
Ogni singolo componente utilizza un sottoinsieme dei 29 ruoli di colore definiti da un
ColorScheme
. Ad esempio, i pulsanti utilizzano fino a quattro colori, che per impostazione predefinita vengono ricavati dal gruppo "principale" del ColorScheme
attivo:
Token del componente ButtonColors |
Ruolo ColorScheme |
---|---|
containerColor | principale |
iconColor | onPrimary |
labelColor | onPrimary |
secondaryLabelColor | onPrimary (opacità 0,8) |
Potresti dover discostarti dai token di colore predefiniti per elementi specifici dell'interfaccia utente. Ad esempio, potresti voler utilizzare un textEdgeButton
con colori del gruppo "secondario" o "terziario" anziché "principale" per risaltare e ottenere un contrasto migliore.
Puoi personalizzare i colori dei componenti in diversi modi:
Utilizza una funzione di supporto per i colori predefiniti. Utilizza funzioni di supporto come
filledTonalButtonColors()
per applicare gli stili dei pulsanti standard per Material 3 Expressive. Queste funzioni creano istanze preconfigurate diButtonColors
che mappano stili comuni come riempiti, a tonalità o con contorni ai ruoli appropriati dell'ColorScheme
attivo all'interno delMaterialScope
. In questo modo, puoi applicare stili coerenti senza dover definire manualmente ogni colore per i tipi di pulsanti comuni.textEdgeButton( colors = filledButtonColors() // default /* OR colors = filledTonalButtonColors() */ /* OR colors = filledVariantButtonColors() */ // ... other parameters )
Per le schede, utilizza la famiglia di funzioni
filledCardColors()
equivalente.Puoi anche modificare l'oggetto
ButtonColors
restituito dalle funzioni di supporto utilizzando il relativo metodocopy()
se devi modificare solo uno o due token:textEdgeButton( colors = filledButtonColors() .copy( containerColor = colorScheme.tertiary, labelColor = colorScheme.onTertiary ) // ... other parameters )
Fornisci esplicitamente i ruoli di colore sostitutivi. Crea il tuo oggetto
ButtonColors
e passalo al componente. Per le carte, utilizza l'oggettoCardColors
equivalente.textEdgeButton( colors = ButtonColors( // the materialScope makes colorScheme available containerColor = colorScheme.secondary, iconColor = colorScheme.secondaryDim, labelColor = colorScheme.onSecondary, secondaryLabelColor = colorScheme.onSecondary ) // ... other parameters )
Specifica colori fissi (da usare con cautela). Sebbene in genere sia consigliato specificare i colori in base al loro ruolo semantico (ad es.
colorScheme.primary
), puoi anche fornire valori di colore diretti. Questo approccio deve essere utilizzato con parsimonia, in quanto può portare a incoerenze con il tema generale, soprattutto se il tema cambia in modo dinamico.textEdgeButton( colors = filledButtonColors().copy( containerColor = android.graphics.Color.RED.argb, // Using named colors labelColor = 0xFFFFFF00.argb // Using a hex code for yellow ) // ... other parameters )
Tipografia
Per creare coerenza visiva sulla piattaforma Wear OS e ottimizzare il rendimento, tutto il testo dei riquadri viene visualizzato utilizzando un carattere fornito dal sistema. ovvero i riquadri non supportano i caratteri personalizzati. Su Wear OS 6 e versioni successive, si tratta di un carattere specifico dell'OEM. Nella maggior parte dei casi si tratta di un carattere variabile, che offre un'esperienza più espressiva e un controllo più granulare.
Per creare uno stile di testo, in genere si utilizza il metodo text()
insieme alle costanti tipografiche. Questo componente ti consente di utilizzare i ruoli di tipografia predefiniti in Material 3 Expressive, che aiutano la tua scheda a rispettare le best practice di tipografia consolidate per leggibilità e gerarchia.
La libreria offre un insieme di 18 costanti di tipografia semantica, come
BODY_MEDIUM. Queste costanti influiscono anche sugli assi dei caratteri diversi dalle dimensioni.
text(
text = "Hello, World!".layoutString,
typography = BODY_MEDIUM,
)
Per un maggiore controllo, puoi fornire impostazioni aggiuntive. Su Wear OS 6 e versioni successive, è probabile che venga utilizzato un carattere variabile, che puoi modificare lungo gli assi italico, peso, larghezza e rotondità. Puoi controllare questi assi
utilizzando il parametro settings
:
text(
text = "Hello, World".layoutString,
italic = true,
// Use elements defined in androidx.wear.protolayout.LayoutElementBuilders.FontSetting
settings =
listOf(weight(500), width(100F), roundness(100)),
)
Infine, se devi controllare le dimensioni o la spaziatura delle lettere (non consigliato),
utilizza basicText() anziché text() e crea un valore per la proprietà
fontStyle
utilizzando fontStyle().
Forma e margini
Puoi modificare il raggio di curvatura degli angoli di quasi tutti i componenti utilizzando la relativa proprietàshape
. I valori provengono dalla proprietà MaterialScope
shapes
:
textButton(
height = expand(),
width = expand(),
shape = shapes.medium, // OR another value like shapes.full
colors = filledVariantButtonColors(),
labelContent = { text("Hello, World!".layoutString) },
)
Dopo aver modificato la forma di un componente, se ritieni che lasci troppo o troppo poco spazio attorno al bordo del display, regola i margini utilizzando il parametro margin
di primaryLayout()
:
primaryLayout(
mainSlot = {
textButton(
shape = shapes.small,
/* ... */
)
},
// margin constants defined in androidx.wear.protolayout.material3.PrimaryLayoutMargins
margins = MAX_PRIMARY_LAYOUT_MARGIN,
)
Archi
Sono supportati i seguenti elementi contenitore secondari Arc
:
ArcLine
: mostra una linea curva attorno all'arco.ArcText
: consente di visualizzare il testo curvo nell'arco.ArcAdapter
: mostra un elemento di layout di base nell'arco, disegnato in corrispondenza di una tangente all'arco.
Per ulteriori informazioni, consulta la documentazione di riferimento per ciascuno dei tipi di elementi.
Modificatori
A ogni elemento di layout disponibile è possibile applicare facoltativamente dei modificatori. Utilizza questi modificatori per le seguenti finalità:
- Modificare l'aspetto visivo del layout. Ad esempio, aggiungi uno sfondo, un bordino o un'area di a capo all'elemento di layout.
- Aggiungi metadati sul layout. Ad esempio, aggiungi un modificatore di semantica all'elemento di layout per l'utilizzo con gli screen reader.
- Aggiungi funzionalità. Ad esempio, aggiungi un modificatore selezionabile all'elemento di layout per rendere interattivo il riquadro. Per ulteriori informazioni, consulta Interagire con i riquadri.
Ad esempio, possiamo personalizzare l'aspetto e i metadati predefiniti di un Image
, come mostrato nell'esempio di codice seguente:
Kotlin
private fun myImage(): LayoutElement = Image.Builder() .setWidth(dp(24f)) .setHeight(dp(24f)) .setResourceId("image_id") .setModifiers(Modifiers.Builder() .setBackground(Background.Builder().setColor(argb(0xFFFF0000)).build()) .setPadding(Padding.Builder().setStart(dp(12f)).build()) .setSemantics(Semantics.builder() .setContentDescription("Image description") .build() ).build() ).build()
Java
private LayoutElement myImage() { return new Image.Builder() .setWidth(dp(24f)) .setHeight(dp(24f)) .setResourceId("image_id") .setModifiers(new Modifiers.Builder() .setBackground(new Background.Builder().setColor(argb(0xFFFF0000)).build()) .setPadding(new Padding.Builder().setStart(dp(12f)).build()) .setSemantics(new Semantics.Builder() .setContentDescription("Image description") .build() ).build() ).build(); }
Espandibili
Un Spannable
è un tipo speciale di contenitore che dispone gli elementi in modo simile al testo. Questa operazione è utile quando vuoi applicare uno stile diverso a una sola
substringa in un blocco di testo più grande, cosa non possibile con l'elemento
Text
.
Un contenitore Spannable
è riempito con Span
elementi secondari. Non sono consentiti altri elementi secondari o istanze Spannable
nidificate.
Esistono due tipi di elementi Span
figlio:
SpanText
: consente di visualizzare il testo con uno stile specifico.SpanImage
: consente di visualizzare un'immagine in linea con il testo.
Ad esempio, puoi mettere in corsivo la parola "world" in un riquadro "Hello world" e inserire un'immagine tra le parole, come mostrato nel seguente esempio di codice:
Kotlin
private fun mySpannable(): LayoutElement = Spannable.Builder() .addSpan(SpanText.Builder() .setText("Hello ") .build() ) .addSpan(SpanImage.Builder() .setWidth(dp(24f)) .setHeight(dp(24f)) .setResourceId("image_id") .build() ) .addSpan(SpanText.Builder() .setText("world") .setFontStyle(FontStyle.Builder() .setItalic(true) .build()) .build() ).build()
Java
private LayoutElement mySpannable() { return new Spannable.Builder() .addSpan(new SpanText.Builder() .setText("Hello ") .build() ) .addSpan(new SpanImage.Builder() .setWidth(dp(24f)) .setHeight(dp(24f)) .setResourceId("image_id") .build() ) .addSpan(new SpanText.Builder() .setText("world") .setFontStyle(newFontStyle.Builder() .setItalic(true) .build()) .build() ).build(); }
Lavorare con le risorse
Le schede non hanno accesso alle risorse della tua app. Ciò significa che non puoi passare un ID immagine Android a un elemento di layout Image
e aspettarti che venga risolto. Sostituisci il metodo onTileResourcesRequest()
e
fornisci le risorse manualmente.
Esistono due modi per fornire immagini all'interno del metodo onTileResourcesRequest()
:
- Fornisci una risorsa drawable utilizzando
setAndroidResourceByResId()
. - Fornisci un'immagine dinamica come
ByteArray
utilizzandosetInlineResource()
.
Kotlin
override fun onTileResourcesRequest( requestParams: ResourcesRequest ) = Futures.immediateFuture( Resources.Builder() .setVersion("1") .addIdToImageMapping("image_from_resource", ImageResource.Builder() .setAndroidResourceByResId(AndroidImageResourceByResId.Builder() .setResourceId(R.drawable.image_id) .build() ).build() ) .addIdToImageMapping("image_inline", ImageResource.Builder() .setInlineResource(InlineImageResource.Builder() .setData(imageAsByteArray) .setWidthPx(48) .setHeightPx(48) .setFormat(ResourceBuilders.IMAGE_FORMAT_RGB_565) .build() ).build() ).build() )
Java
@Override protected ListenableFuture<Resources> onTileResourcesRequest( @NonNull ResourcesRequest requestParams ) { return Futures.immediateFuture( new Resources.Builder() .setVersion("1") .addIdToImageMapping("image_from_resource", new ImageResource.Builder() .setAndroidResourceByResId(new AndroidImageResourceByResId.Builder() .setResourceId(R.drawable.image_id) .build() ).build() ) .addIdToImageMapping("image_inline", new ImageResource.Builder() .setInlineResource(new InlineImageResource.Builder() .setData(imageAsByteArray) .setWidthPx(48) .setHeightPx(48) .setFormat(ResourceBuilders.IMAGE_FORMAT_RGB_565) .build() ).build() ).build() ); }
Elenco di controllo delle immagini di anteprima dei riquadri
Il sistema mostra l'immagine di anteprima del riquadro, a cui si fa riferimento nel file manifest dell'app Android, nell'editor del carosello di riquadri. Questo editor viene visualizzato sia sui dispositivi Wear OS sia nell'app complementare dello smartwatch sugli smartphone.
Per aiutare gli utenti a sfruttare al meglio questa immagine di anteprima, verifica i seguenti dettagli del riquadro:
- Riflette il design più recente. L'anteprima deve rappresentare con precisione il design più recente della tua scheda.
- Utilizza un tema di colore statico. Utilizza il tema a colori statico della scheda, non uno dinamico.
- Include l'icona dell'app. Verifica che l'icona dell'app venga visualizzata nella parte superiore dell'immagine di anteprima.
- Mostra lo stato caricato/di accesso. L'anteprima deve mostrare uno stato "caricato" o "accesso eseguito" completamente funzionale, evitando contenuti vuoti o segnaposto.
- (Facoltativo) Utilizza le regole di risoluzione delle risorse per la personalizzazione. Valuta la possibilità di utilizzare le regole di risoluzione delle risorse di Android per fornire anteprime che corrispondano alle impostazioni di lingua, dimensioni del display o impostazioni internazionali del dispositivo. Questo è particolarmente utile se l'aspetto del riquadro varia da un dispositivo all'altro.