Começar a usar Blocos

Para começar a fornecer blocos, inclua as dependências abaixo no arquivo build.gradle do 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")
}

Principais conceitos

Os Blocos não são criados da mesma forma que os apps Android e usam conceitos diferentes:

  • Modelos de layout:definem a disposição geral dos elementos visuais na tela. Um bloco usa um modelo de layout EdgeContentLayout, que inclui um indicador de progresso ao redor da borda da tela, ou um PrimaryLayout, que não mostra esse indicador.
  • Elementos de layout:representam um elemento gráfico individual, como Button ou Chip, ou vários elementos agrupados usando um Column, MultiButtonLayout, MultiSlotLayout ou algo semelhante. Eles são incorporados em um modelo de layout.
  • Recursos:os objetos ResourceBuilders.Resources consistem em um mapa de pares de chave-valor dos recursos do Android (imagens) necessários para renderizar um layout e uma versão.
  • Linha do tempo:um objeto TimelineBuilders.Timeline é uma lista de uma ou mais instâncias de um objeto de layout. É possível fornecer vários mecanismos e expressões para indicar quando o renderizador precisa alternar de um objeto de layout para outro, como parar de mostrar um layout em um momento específico.
  • Estado:uma estrutura de dados do tipo StateBuilders.State que é transmitida entre o Bloco e o app para permitir que os dois componentes se comuniquem entre si. Por exemplo, se um botão for tocado no bloco, o estado terá o ID do botão. Também é possível trocar tipos de dados usando um mapa.
  • Tile:um objeto TileBuilders.Tile que representa um bloco, consistindo de uma linha do tempo, um ID da versão de recursos, um intervalo de atualização e um estado.
  • Protolayout:esse termo aparece no nome de várias classes relacionadas a blocos e se refere à biblioteca Protolayout do Wear OS, uma biblioteca gráfica usada em várias plataformas do Wear OS.

Criar um bloco

Para fornecer um Bloco do app, implemente um serviço do tipo TileService e registre-o no manifesto. Com isso, o sistema solicita os tiles necessários durante as chamadas para onTileRequest() e recursos durante as chamadas para 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()
        )
}

Em seguida, adicione um serviço dentro da tag <application> do arquivo 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>

O filtro de permissão e intent registra esse serviço como um provedor de blocos.

O ícone, o rótulo, a descrição e o recurso de visualização são mostrados ao usuário quando ele configura Blocos no smartphone ou smartwatch.

Implante o app e adicione o Bloco ao carrossel de Blocos. Há uma maneira mais fácil para desenvolvedores visualizarem um Bloco, mas, por enquanto, faça manualmente.

Figura 1. Bloco "Hello World".

Para conferir um exemplo completo, consulte o exemplo de código no GitHub ou o codelab.

Criar uma IU para blocos

O layout de um bloco é criado usando um padrão de construtor. Ele é construído como árvore que consiste em contêineres e elementos básicos de layout. Cada elemento de layout tem propriedades, que podem ser definidas com vários métodos setter.

Elementos básicos de layout

Os elementos visuais da biblioteca protolayout abaixo têm suporte nos componentes do Material Design:

  • Text: renderiza uma string de texto, opcionalmente a encapsulando.
  • Image: renderiza uma imagem.
  • Spacer: fornece padding entre elementos ou pode atuar como um divisor quando você define a cor do plano de fundo.

Componentes do Material Design

Além dos elementos básicos, a biblioteca protolayout-material fornece componentes que garantem um design de blocos alinhado às recomendações da interface do usuário do Material Design.

  • Button: componente circular clicável projetado para conter um ícone.
  • Chip: componente clicável em formato de estádio projetado para conter até duas linhas de texto e um ícone opcional.

  • CompactChip: componente clicável em formato de estádio projetado para conter uma linha de texto.

  • TitleChip: componente clicável em formato de estádio semelhante a Chip, mas com uma altura maior para acomodar o texto do título.

  • CircularProgressIndicator: indicador de progresso circular que pode ser colocado dentro de um EdgeContentLayout para mostrar o progresso ao redor das bordas da tela.

Contêineres de layout

Os contêineres abaixo têm suporte nos layouts do Material Design:

  • Row: mostra os elementos filhos horizontalmente, um após o outro.
  • Column: mostra os elementos filhos verticalmente, um após o outro.
  • Box: sobrepõe elementos filhos uns sobre os outros.
  • Arc: mostra os elementos filhos em um círculo.
  • Spannable: aplica FontStyles específicos a seções de texto, bem como texto e imagens intercalados. Para mais informações, consulte Spannables.

Cada contêiner pode conter um ou mais filhos, que também podem ser contêineres. Por exemplo, uma Column pode conter vários elementos Row como filhos, resultando em um layout parecido com uma grade.

Um bloco com um layout de contêiner e dois elementos de layout filhos pode ser parecido com este exemplo:

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

Layouts do Material Design

Além dos layouts básicos, a biblioteca protolayout-material fornece alguns layouts opinativos feitos para manter elementos em "slots" específicos.

  • PrimaryLayout: posiciona uma única ação principal CompactChip na parte de baixo com o conteúdo centralizado acima dela.

  • MultiSlotLayout: posiciona os rótulos primário e secundário com conteúdo opcional entre eles e um CompactChip opcional na parte de baixo da tela.

  • MultiButtonLayout: posiciona um conjunto de botões organizados de acordo com as diretrizes do Material Design.

  • EdgeContentLayout: posiciona o conteúdo ao redor da borda de uma tela, como um CircularProgressIndicator. Ao usar esse layout, o conteúdo dele tem as margens e o padding adequados aplicados automaticamente.

Arcos

Há suporte para os filhos de contêiner Arc abaixo:

  • ArcLine: renderiza uma linha curva ao redor do arco.
  • ArcText: renderiza texto curvado no arco.
  • ArcAdapter: renderiza um elemento de layout básico no arco, mostrado em uma tangente em relação ao arco.

Para mais informações, consulte a documentação de referência de cada um dos tipos de elemento.

Modificadores

Como alternativa, todos os elementos de layout disponíveis podem ter modificadores. Use esses modificadores para as finalidades abaixo:

  • Mudar a aparência do layout. Por exemplo, adicionar um plano de fundo, uma borda ou um padding ao elemento de layout.
  • Adicionar metadados sobre o layout. Por exemplo, adicionar um modificador de semântica ao elemento de layout que vai ser usado por leitores de tela.
  • Adicionar funcionalidade. Por exemplo, adicione um modificador clicável ao elemento de layout para tornar o bloco interativo. Para mais informações, consulte Interagir com blocos.

Por exemplo, podemos personalizar a aparência e os metadados padrão de uma Image, conforme mostrado no exemplo de código abaixo:

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

Spannables

Um Spannable é um tipo especial de contêiner que apresenta elementos de maneira parecida com um texto. Isso é útil quando você quer aplicar um estilo diferente a apenas uma substring em um bloco de texto maior, algo que não é possível com o elemento Text.

Um contêiner Spannable é preenchido com filhos Span. Outros filhos ou instâncias Spannable aninhadas não são permitidos.

Há dois tipos de filhos Span:

  • SpanText: renderiza o texto com um estilo específico.
  • SpanImage: renderiza uma imagem inline com texto.

Por exemplo, você pode aplicar itálico à palavra "world" em um bloco "Hello world" e inserir uma imagem entre as palavras, conforme mostrado neste exemplo de código:

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

Trabalhar com recursos

Os blocos não têm acesso a nenhum dos recursos do seu app. Isso significa que você não pode transmitir um ID de imagem do Android a um elemento de layout Image e esperar que ele seja resolvido. Em vez disso, substitua o método onTileResourcesRequest() e forneça os recursos manualmente.

Há duas maneiras de fornecer imagens no método 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()
);
}