Inizia a utilizzare i riquadri

Per iniziare a fornire riquadri dalla tua app, includi le seguenti dipendenze nel file build.gradle della tua app.

Groovy

dependencies {
    // Use to implement support for wear tiles
    implementation "androidx.wear.tiles:tiles:1.5.0-alpha04"

    // Use to utilize standard components and layouts in your tiles
    implementation "androidx.wear.protolayout:protolayout:1.3.0-alpha04"

    // Use to utilize components and layouts with Material Design in your tiles
    implementation "androidx.wear.protolayout:protolayout-material:1.3.0-alpha04"

    // Use to include dynamic expressions in your tiles
    implementation "androidx.wear.protolayout:protolayout-expression:1.3.0-alpha04"

    // Use to preview wear tiles in your own app
    debugImplementation "androidx.wear.tiles:tiles-renderer:1.5.0-alpha04"

    // Use to fetch tiles from a tile provider in your tests
    testImplementation "androidx.wear.tiles:tiles-testing:1.5.0-alpha04"
}

Kotlin

dependencies {
    // Use to implement support for wear tiles
    implementation("androidx.wear.tiles:tiles:1.5.0-alpha04")

    // Use to utilize standard components and layouts in your tiles
    implementation("androidx.wear.protolayout:protolayout:1.3.0-alpha04")

    // Use to utilize components and layouts with Material Design in your tiles
    implementation("androidx.wear.protolayout:protolayout-material:1.3.0-alpha04")

    // Use to include dynamic expressions in your tiles
    implementation("androidx.wear.protolayout:protolayout-expression:1.3.0-alpha04")

    // Use to preview wear tiles in your own app
    debugImplementation("androidx.wear.tiles:tiles-renderer:1.5.0-alpha04")

    // Use to fetch tiles from a tile provider in your tests
    testImplementation("androidx.wear.tiles:tiles-testing:1.5.0-alpha04")
}

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. Un riquadro utilizza un modello di layout EdgeContentLayout, che include un indicatore di avanzamento attorno al bordo del display, o un PrimaryLayout, che non mostra questo indicatore.
  • Elementi di layout: rappresentano un singolo elemento grafico, ad esempio Button o Chip, o più elementi raggruppati insieme utilizzando Column, MultiButtonLayout, MultiSlotLayout o simili. 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 e 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(
                        Text.Builder(this, "Hello World!")
                            .setTypography(Typography.TYPOGRAPHY_BODY1)
                            .setColor(argb(0xFFFFFFFF.toInt()))
                            .build()
                    )
                )
                .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.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.

Per un esempio completo, consulta il codice di esempio su GitHub o nel codelab.

Panoramica del ciclo di vita del servizio dei riquadri

Dopo aver creato e dichiarato TileService nel file manifest dell'app, puoi rispondere alle modifiche dello stato del servizio dei riquadri.

TileService è un servizio associato. Il tuo TileService è vincolato come risultato della richiesta dell'app o se il sistema deve comunicare con esso. Un tipico ciclo di vita del servizio associato contiene i seguenti quattro metodi di callback: onCreate(), onBind(), onUnbind() e onDestroy(). Il sistema richiama questi metodi ogni volta che il servizio entra in una nuova fase del ciclo di vita.

Oltre ai callback che controllano il ciclo di vita del servizio associato, puoi implementare altri metodi specifici per il ciclo di vita di TileService. Tutti i servizi di riquadri devono implementare onTileRequest() e onTileResourcesRequest() per rispondere alle richieste di aggiornamento del sistema.

  • onTileAddEvent(): il sistema chiama questo metodo solo quando l'utente aggiunge il riquadro per la prima volta e se l'utente rimuove e aggiunge di nuovo il riquadro. Questo è il momento migliore per eseguire qualsiasi inizializzazione una tantum.

    onTileAddEvent() viene chiamato solo quando l'insieme di riquadri viene riconfigurato, non ogni volta che un riquadro viene creato dal sistema. Ad esempio, quando il dispositivo viene riavviato o acceso, onTileAddEvent() non viene chiamato per le schede già aggiunte. Puoi utilizzare getActiveTilesAsync() per ottenere uno snapshot dei riquadri di tua proprietà attivi.

  • onTileRemoveEvent(): il sistema chiama questo metodo solo se l'utenterimuove il riquadro.

  • onTileEnterEvent(): il sistema chiama questo metodo quando un riquadro fornito da questo fornitore viene visualizzato sullo schermo.

  • onTileLeaveEvent(): il sistema chiama questo metodo quando una scheda fornito da questo fornitore non è più visibile sullo schermo.

  • onTileRequest(): il sistema chiama questo metodo quando richiede un nuovo programma a questo fornitore.

  • onTileResourcesRequest(): il sistema chiama questo metodo quando richiede un bundle di risorse a questo fornitore. Ciò può accadere la prima volta che viene caricato un riquadro o ogni volta che la versione della risorsa cambia.

Esegui query sui riquadri attivi

Le schede attive sono schede che sono state aggiunte per essere visualizzate sullo smartwatch. Utilizza il metodo statico getActiveTilesAsync() di TileService per eseguire query sui riquadri appartenenti alla tua app attivi.

Creare l'interfaccia utente per le schede

Il layout di un riquadro viene scritto utilizzando un pattern di builder. Il layout di una scheda è costituito da un albero composto da contenitori di layout ed elementi di layout di base. Ogni elemento di layout ha proprietà che puoi impostare tramite vari metodi setter.

Elementi di layout di base

Sono supportati i seguenti elementi visivi della raccolta protolayout, insieme ai componenti di Material:

  • Text: visualizza una stringa di testo, eventualmente con a capo.
  • Image: esegue il rendering di un'immagine.
  • Spacer: fornisce spaziatura tra gli elementi o può fungere da divisore quando imposti il colore di sfondo.

Componenti di materiale

Oltre agli elementi di base, la libreria protolayout-material fornisce componenti che garantiscono un design delle schede in linea con i consigli per l'interfaccia utente di Material Design.

  • Button: componente circolare selezionabile progettato per contenere un'icona.
  • Chip: componente a forma di stadio cliccabile progettato per contenere fino a due righe di testo e un'icona facoltativa.

  • CompactChip: componente a forma di stadio cliccabile progettato per contenere una riga di testo.

  • TitleChip: componente a forma di stadio cliccabile simile a Chip, ma con un'altezza maggiore per adattarsi al testo del titolo.

  • CircularProgressIndicator: indicatore di avanzamento circolare che può essere inserito in un EdgeContentLayout per visualizzare l'avanzamento lungo i bordi dello schermo.

Container di layout

I seguenti contenitori sono supportati, insieme ai layout Material:

  • Row: dispone gli elementi secondari in orizzontale, uno dopo l'altro.
  • Column: dispone gli elementi secondari in verticale, uno dopo l'altro.
  • Box: sovrappone gli elementi secondari uno sopra l'altro.
  • Arc: dispone gli elementi secondari in un cerchio.
  • Spannable: applica FontStyles specifici a sezioni di testo, nonché interlaccia testo e immagini. Per ulteriori informazioni, consulta Spannables.

Ogni contenitore può contenere uno o più elementi secondari, che possono essere anche essi dei contenitori. Ad esempio, un elemento Column può contenere più elementi Row come elementi figli, generando un layout simile a una griglia.

Ad esempio, un riquadro con un layout del contenitore e due elementi di layout secondari potrebbe avere il seguente aspetto:

Kotlin

private fun myLayout(): LayoutElement =
    Row.Builder()
        .setWidth(wrap())
        .setHeight(expand())
        .setVerticalAlignment(VERTICAL_ALIGN_BOTTOM)
        .addContent(Text.Builder()
            .setText("Hello world")
            .build()
        )
        .addContent(Image.Builder()
            .setResourceId("image_id")
            .setWidth(dp(24f))
            .setHeight(dp(24f))
            .build()
        ).build()

Java

private LayoutElement myLayout() {
    return new Row.Builder()
        .setWidth(wrap())
        .setHeight(expand())
        .setVerticalAlignment(VALIGN_BOTTOM)
        .addContent(new Text.Builder()
            .setText("Hello world")
            .build()
        )
        .addContent(new Image.Builder()
            .setResourceId("image_id")
            .setWidth(dp(24f))
            .setHeight(dp(24f))
            .build()
        ).build();
}

Layout dei materiali

Oltre ai layout di base, la libreria protolayout-material fornisce alcuni layout con opinioni che hanno lo scopo di contenere elementi in "slot" specifici.

  • PrimaryLayout: posiziona una singola azione principale CompactChip in basso con i contenuti al centro sopra.

  • MultiSlotLayout: posiziona le etichette principali e secondarie con contenuti facoltativi in mezzo e un CompactChip facoltativo in basso.

  • MultiButtonLayout: posiziona un insieme di pulsanti disposti in base alle linee guida di Material.

  • EdgeContentLayout: posiziona i contenuti attorno al bordo di una schermata, come un CircularProgressIndicator. Quando utilizzi questo layout, ai contenuti al suo interno vengono applicati automaticamente i margini e i padding appropriati.

Archi

I seguenti elementi contenitore secondari Arc sono supportati:

  • 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à:

  • Modifica 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 sottostringa 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():

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()
);
}