Inizia a utilizzare i riquadri

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

Trendy

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

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

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

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

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

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

Kotlin

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

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

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

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

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

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

Crea un riquadro

Per fornire un riquadro dalla tua applicazione, crea una classe che estenda TileService e implementa i metodi, come mostrato nel seguente esempio di codice:

Kotlin

// Uses the ProtoLayout namespace for tile timeline objects.
// If you haven't done so already, migrate to the ProtoLayout namespace.
import androidx.wear.protolayout.TimelineBuilders.Timeline
import androidx.wear.protolayout.material.Text
import androidx.wear.tiles.TileBuilders.Tile

private val RESOURCES_VERSION = "1"
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_DISPLAY1)
                        .setColor(argb(0xFF000000.toInt()))
                        .build()))
            .build())

    override fun onTileResourcesRequest(requestParams: ResourcesRequest) =
        Futures.immediateFuture(Resources.Builder()
            .setVersion(RESOURCES_VERSION)
            .build()
        )
}

Java

// Uses the ProtoLayout namespace for tile timeline objects.
// If you haven't done so already, migrate to the ProtoLayout namespace.
import androidx.wear.protolayout.TimelineBuilders.Timeline;
import androidx.wear.protolayout.material.Text;
import androidx.wear.tiles.TileBuilders.Tile;

public class MyTileService extends TileService {
    private static final String RESOURCES_VERSION = "1";

    @NonNull
    @Override
    protected ListenableFuture<Tile> onTileRequest(
        @NonNull TileRequest requestParams
    ) {
        return Futures.immediateFuture(new Tile.Builder()
            .setResourcesVersion(RESOURCES_VERSION)
            .setTileTimeline(
                Timeline.fromLayoutElement(
                    new Text.Builder(this, "Hello world!")
                        .setTypography(Typography.TYPOGRAPHY_DISPLAY1)
                        .setColor(ColorBuilders.argb(0xFF000000))
                        .build()))
            .build()
        );
   }

   @NonNull
   @Override
   protected ListenableFuture<Resources> onTileResourcesRequest(
       @NonNull ResourcesRequest requestParams
   ) {
       return Futures.immediateFuture(new Resources.Builder()
               .setVersion(RESOURCES_VERSION)
               .build()
       );
   }
}

Quindi, aggiungi un servizio all'interno del tag <application> del file AndroidManifest.xml.

<service
   android:name=".MyTileService"
   android:label="@string/tile_label"
   android:description="@string/tile_description"
   android:icon="@drawable/tile_icon_round"
   android:roundIcon="@drawable/tile_icon_round"
   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 di autorizzazione e intent registra questo servizio come fornitore di riquadri.

L'icona, l'etichetta e la descrizione vengono mostrate all'utente quando configura i riquadri sul suo smartphone o smartwatch.

Utilizza il tag metadati di anteprima per mostrare un'anteprima del riquadro durante la configurazione sul telefono.

Panoramica del ciclo di vita del servizio riquadri

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

TileService è un servizio associato. TileService è vincolato a seguito della richiesta della tua app o se il sistema deve comunicare con l'app. Un tipico ciclo di vita di bound-service include i seguenti quattro metodi di callback: onCreate(), onBind(), onUnbind() e onDestroy(). Il sistema li richiama ogni volta che il servizio entra in una nuova fase del ciclo di vita.

Oltre ai callback che controllano il ciclo di vita di bound-service, puoi implementare altri metodi specifici del ciclo di vita di TileService. Tutti i servizi dei riquadri devono implementare onTileRequest() e onTileResourcesRequest() per rispondere alle richieste di aggiornamenti dal sistema.

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

    onTileAddEvent() viene chiamato solo quando l'insieme di riquadri viene riconfigurato, non quando un riquadro viene creato dal sistema. Ad esempio, quando il dispositivo viene riavviato o acceso, onTileAddEvent() non viene chiamato per i riquadri già aggiunti. Puoi utilizzare getActiveTilesAsync() per ottenere un'istantanea dei riquadri di tua proprietà attivi.

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

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

  • onTileLeaveEvent(): il sistema chiama questo metodo quando un riquadro fornito da questo fornitore non è visibile sullo schermo.

  • onTileRequest(): il sistema chiama questo metodo quando il sistema richiede una nuova cronologia al fornitore.

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

Query sui riquadri attivi

I riquadri attivi sono riquadri che sono stati aggiunti per essere visualizzati sull'orologio. Usa il metodo statico getActiveTilesAsync() di TileService per eseguire query sui riquadri appartenenti alla tua app attivi.

Crea UI per i riquadri

Il layout di un riquadro è scritto utilizzando un pattern di creazione. Il layout di un riquadro è costituito da un albero, costituito da contenitori di layout ed elementi di layout di base. Ogni elemento del layout ha delle proprietà che puoi impostare con vari metodi di setter.

Elementi del layout di base

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

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

Componenti del materiale

Oltre agli elementi di base, la libreria protolayout-material fornisce componenti che garantiscono un design dei riquadri in linea con i suggerimenti dell'interfaccia utente di Material Design.

  • Button: componente circolare cliccabile progettato per contenere un'icona.
  • Chip: componente cliccabile a forma di stadio 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 contenere il testo del titolo.

  • CircularProgressIndicator: indicatore di avanzamento circolare che può essere posizionato all'interno di un elemento EdgeContentLayout per visualizzare l'avanzamento intorno ai bordi dello schermo.

Contenitori di layout

Sono supportati i seguenti container, insieme ai layout dei materiali:

  • Row: dispone gli elementi secondari orizzontalmente, uno dopo l'altro.
  • Column: dispone gli elementi secondari verticalmente, uno dopo l'altro.
  • Box: consente di sovrapporre gli elementi secondari uno sopra l'altro.
  • Arc: dispone gli elementi secondari in un cerchio.
  • Spannable: si applica a sezioni di testo specifiche FontStyles insieme a testo e immagini interfolianti. Per ulteriori informazioni, consulta la sezione Spannable.

Ogni container può contenere uno o più elementi secondari, che a loro volta possono essere anche container. Ad esempio, un elemento Column può contenere più elementi Row come elementi secondari, il che genera un layout di griglia.

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

Kotlin

private fun myLayout(): LayoutElement =
    Row.Builder()
        .setWidth(wrap())
        .setHeight(expand())
        .setVerticalAlignment(VALIGN_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 di opinione pensati per contenere elementi in "slot" specifici.

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

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

  • MultiButtonLayout: consente di posizionare un insieme di pulsanti disposti secondo le linee guida sui materiali.

  • EdgeContentLayout: posiziona i contenuti intorno al bordo dello schermo, ad esempio un CircularProgressIndicator. Quando usi questo layout, ai contenuti al suo interno vengono applicati automaticamente i margini e la spaziatura interna appropriati.

Archi

Sono supportati i seguenti container Arc secondari:

  • ArcLine: esegue il rendering di una linea curva intorno all'arco.
  • ArcText: visualizza il testo inclinato nell'arco.
  • ArcAdapter: visualizza un elemento di layout di base nell'arco, disegnato in base a una tangente all'arco.

Per ulteriori informazioni, consulta la documentazione di riferimento per ciascun tipo di elemento.

Modificatori

A ogni elemento di layout disponibile è possibile applicare facoltativamente modificatori. Utilizza questi modificatori per i seguenti scopi:

  • Modificare l'aspetto visivo del layout. Ad esempio, aggiungi uno sfondo, un bordo o una spaziatura interna all'elemento del layout.
  • Aggiungi metadati relativi al layout. Ad esempio, aggiungi un modificatore di semantica all'elemento del layout per utilizzarlo con gli screen reader.
  • Aggiungi funzionalità. Ad esempio, aggiungi un modificatore cliccabile all'elemento di layout per rendere interattivo il riquadro. Per maggiori informazioni, consulta Interagire con i riquadri.

Ad esempio, possiamo personalizzare l'aspetto e i metadati predefiniti di una Image, come mostrato nel seguente esempio di codice:

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

Estraibili

Un Spannable è un tipo speciale di contenitore che dispone gli elementi in modo simile al testo. Questo è utile quando vuoi applicare uno stile diverso a una sola sottostringa in un blocco di testo più grande, cosa che non è possibile con l'elemento Text.

Un container Spannable contiene Span elementi secondari. Non sono consentiti altri elementi secondari o istanze Spannable nidificate.

Esistono due tipi di elementi secondari Span:

  • SpanText: visualizza il testo con uno stile specifico.
  • SpanImage: visualizza un'immagine in linea con il testo.

Ad esempio, puoi mettere in corsivo la parola "mondo" 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

I riquadri non hanno accesso ad alcuna risorsa della tua app. Ciò significa che non puoi passare un ID immagine Android a un elemento di layout Image e aspettarti che venga risolto. Puoi invece sostituire il metodo onTileResourcesRequest() e fornire le risorse manualmente.

Esistono due modi per fornire immagini con il 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()
);
}