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.4.0" // Use to utilize standard components and layouts in your tiles implementation "androidx.wear.protolayout:protolayout:1.2.0" // Use to utilize components and layouts with Material Design in your tiles implementation "androidx.wear.protolayout:protolayout-material:1.2.0" // Use to include dynamic expressions in your tiles implementation "androidx.wear.protolayout:protolayout-expression:1.2.0" // Use to preview wear tiles in your own app debugImplementation "androidx.wear.tiles:tiles-renderer:1.4.0" // Use to fetch tiles from a tile provider in your tests testImplementation "androidx.wear.tiles:tiles-testing:1.4.0" }
Kotlin
dependencies { // Use to implement support for wear tiles implementation("androidx.wear.tiles:tiles:1.4.0") // Use to utilize standard components and layouts in your tiles implementation("androidx.wear.protolayout:protolayout:1.2.0") // Use to utilize components and layouts with Material Design in your tiles implementation("androidx.wear.protolayout:protolayout-material:1.2.0") // Use to include dynamic expressions in your tiles implementation("androidx.wear.protolayout:protolayout-expression:1.2.0") // Use to preview wear tiles in your own app debugImplementation("androidx.wear.tiles:tiles-renderer:1.4.0") // Use to fetch tiles from a tile provider in your tests testImplementation("androidx.wear.tiles:tiles-testing:1.4.0") }
Criar um bloco
Para fornecer um bloco pelo aplicativo, crie uma classe que estenda
TileService
e implemente os métodos, conforme mostrado neste exemplo de código:
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() ); } }
Em seguida, adicione um serviço dentro da tag <application>
do
arquivo 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>
O filtro de permissão e intent registra esse serviço como um provedor de blocos.
O ícone, o rótulo e a descrição são mostrados ao usuário quando ele configura os blocos no smartphone ou smartwatch.
Use a tag de metadados de visualização para mostrar uma prévia do bloco durante a configuração dele no smartphone.
A visão geral do ciclo de vida do serviço de bloco
Depois de criar e declarar o TileService
no manifesto do app, você pode
pode responder às mudanças de estado do serviço de bloco.
TileService
é um serviço vinculado. Como resultado, seu TileService
está vinculado
da solicitação do app ou se o sistema precisa se comunicar com ele. Uma configuração típica
bound-service lifecycle contém os quatro métodos de callback a seguir:
onCreate()
, onBind()
, onUnbind()
e
onDestroy()
. O sistema invoca esses métodos sempre que o serviço
entra em uma nova fase do ciclo de vida.
Além dos callbacks que controlam o ciclo de vida do serviço vinculado, é possível
implementar outros métodos específicos para o ciclo de vida da TileService
. Todos os blocos
serviços precisam implementar onTileRequest()
e onTileResourcesRequest()
para
responder a solicitações de atualizações do sistema.
onTileAddEvent()
: o sistema chama esse método somente quando o usuário adicionar seu bloco pela primeira vez e, se o usuário remover e adicionar seu bloco, o bloco novamente. Esse é o melhor momento para fazer qualquer inicialização única.O
onTileAddEvent()
só é chamado quando o conjunto de blocos é reconfigurado. sempre que um bloco é criado pelo sistema. Por exemplo, quando o dispositivo for reinicializado ou ligado, o métodoonTileAddEvent()
não será chamado para os blocos; que já foi adicionada. Você pode usargetActiveTilesAsync()
em vez disso para conferir um resumo de quais blocos pertencentes a você estão ativos.onTileRemoveEvent()
: o sistema chamará esse método somente se o usuário remove seu bloco.onTileEnterEvent()
: o sistema chama esse método quando um bloco fornecidos por esse provedor aparecem na tela.onTileLeaveEvent()
: o sistema chama esse método quando um bloco fornecido por este provedor sai da visualização na tela.onTileRequest()
: o sistema chama esse método quando o sistema solicita uma nova linha do tempo deste provedor.onTileResourcesRequest()
: o sistema chama esse método quando o o sistema solicita um pacote de recursos desse provedor. Isso pode acontecer na primeira vez que um bloco é carregado ou sempre que a versão do recurso mudanças.
Consultar quais blocos estão ativos
Blocos ativos são blocos adicionados para exibição no relógio. Usar
Método estático de TileService
getActiveTilesAsync()
para consultar quais blocos
pertencem ao seu app estão ativas.
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 parecido com umChip
, mas com uma altura maior para acomodar o texto do título.CircularProgressIndicator
: indicador circular que pode ser colocado dentro de umEdgeContentLayout
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
: aplicaFontStyles
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(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(); }
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 principalCompactChip
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 umCompactChip
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 umCircularProgressIndicator
. 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 básico de layout no arco, mostrado em uma tangente em relação ao arco.
Para saber mais, 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
neste exemplo de código:
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()
:
- Forneça um recurso drawable usando
setAndroidResourceByResId()
. - Forneça uma imagem dinâmica como uma
ByteArray
usandosetInlineResource()
.
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() ); }
Recomendados para você
- Observação: o texto do link aparece quando o JavaScript está desativado.
- Migrar para namespaces ProtoLayout
ConstraintLayout
no Compose- Criar blocos personalizados das Configurações rápidas para o app