Em dispositivos Wear OS, os blocos são renderizados por dois componentes principais com controle de versão independente. Para ajudar os blocos do app a funcionar corretamente em todos os dispositivos, é importante entender essa arquitetura subjacente.
- Bibliotecas relacionadas a blocos do Jetpack: essas bibliotecas (incluindo Wear Tiles
e Wear ProtoLayout) são incorporadas ao seu app, e você, como desenvolvedor,
controla as versões delas. O app usa essas bibliotecas para construir um objeto
TileBuilder.Tile(a estrutura de dados que representa o bloco) em resposta à chamadaonTileRequest()do sistema. - Renderizador ProtoLayout:esse componente do sistema é responsável por renderizar o objeto
Tilena tela e processar as interações do usuário. A versão do renderizador não é controlada pelo desenvolvedor do app e pode variar entre dispositivos, mesmo aqueles com hardware idêntico.
A aparência ou o comportamento de um bloco podem variar de acordo com as versões da biblioteca Jetpack Tiles do seu app e a versão do renderizador ProtoLayout no dispositivo do usuário. Por exemplo, um dispositivo pode oferecer suporte à rotação ou à exibição de dados de frequência cardíaca, enquanto outro não.
Este documento explica como tornar seu app compatível com diferentes versões da biblioteca Tiles e do renderizador ProtoLayout. Ele também explica como migrar para versões mais recentes da biblioteca Jetpack.
Considere a compatibilidade
Para criar um bloco que funcione corretamente em vários dispositivos, considere a compatibilidade com recursos variados. Isso pode ser feito com duas estratégias principais: detectar recursos do renderizador no tempo de execução e fornecer substituições integradas.
Detectar recursos do renderizador
Você pode mudar dinamicamente o layout do bloco com base nos recursos disponíveis em um determinado dispositivo.
Detectar a versão do renderizador
- Use o método
getRendererSchemaVersion()do objetoDeviceParameterstransmitido ao métodoonTileRequest(). Esse método retorna os números de versão principal e secundária do renderizador ProtoLayout no dispositivo. - Em seguida, use a lógica condicional na implementação do
onTileRequest()para adaptar o design ou o comportamento do bloco com base na versão do renderizador detectada.
A anotação @RequiresSchemaVersion
- A anotação
@RequiresSchemaVersionem métodos ProtoLayout indica a versão mínima do esquema de renderização necessária para que o método se comporte conforme documentado (exemplo).- Chamar um método que exige uma versão do renderizador mais recente do que a disponível no dispositivo não causa falha no app, mas pode fazer com que o conteúdo não seja exibido ou o recurso seja ignorado.
Exemplo de detecção de versão
val rendererVersion = requestParams.deviceConfiguration.rendererSchemaVersion val arcElement = // DashedArcLine has the annotation @RequiresSchemaVersion(major = 1, minor = 500) // and so is supported by renderer versions 1.500 and greater if ( rendererVersion.major > 1 || (rendererVersion.major == 1 && rendererVersion.minor >= 500) ) { // Use DashedArcLine if the renderer supports it … DashedArcLine.Builder() .setLength(degrees(270f)) .setThickness(8f) .setLinePattern( LayoutElementBuilders.DashedLinePattern.Builder() .setGapSize(8f) .setGapInterval(10f) .build() ) .build() } else { // … otherwise use ArcLine. ArcLine.Builder().setLength(degrees(270f)).setThickness(dp(8f)).build() }
Fornecer substituições
Alguns recursos permitem definir um substituto diretamente no builder. Essa abordagem é geralmente mais simples do que verificar a versão do renderizador e é a abordagem preferida quando disponível.
Um caso de uso comum é fornecer uma imagem estática como substituta para uma animação Lottie. Se o dispositivo não for compatível com animações Lottie, ele vai renderizar a imagem estática.
val lottieImage = ResourceBuilders.ImageResource.Builder() .setAndroidLottieResourceByResId( ResourceBuilders.AndroidLottieResourceByResId.Builder(R.raw.lottie) .setStartTrigger(createOnVisibleTrigger()) .build() ) // Fallback if lottie is not supported .setAndroidResourceByResId( ResourceBuilders.AndroidImageResourceByResId.Builder() .setResourceId(R.drawable.lottie_fallback) .build() ) .build()
Teste com diferentes versões do renderizador
Para testar seus blocos em diferentes versões do renderizador, implante-os em diferentes versões do emulador do Wear OS. Em dispositivos físicos, as atualizações do ProtoLayout Renderer são fornecidas pela Play Store ou por atualizações do sistema. Não é possível forçar a instalação de uma versão específica do renderizador.)
O recurso de prévia de bloco do Android Studio usa um renderizador incorporado à biblioteca ProtoLayout do Jetpack de que seu código depende. Portanto, outra abordagem é depender de diferentes versões da biblioteca do Jetpack ao testar blocos.
Migrar para Tiles 1.5 / ProtoLayout 1.3 (Material 3 Expressive)
Atualize as bibliotecas de blocos do Jetpack para aproveitar os aprimoramentos mais recentes, incluindo mudanças na interface para integrar os blocos perfeitamente ao sistema.
O Jetpack Tiles 1.5 e o Jetpack ProtoLayout 1.3 introduzem várias melhorias e mudanças notáveis. São eles:
- Uma API semelhante ao Compose para descrever a interface.
- Componentes Material 3 Expressive, incluindo o botão de borda inferior e suporte a recursos visuais aprimorados: animações Lottie, mais tipos de gradiente e novos estilos de linha de arco. - Observação: alguns desses recursos também podem ser usados sem migrar para a nova API.
Recomendações
Siga estas recomendações ao migrar seus blocos:
- Migre todos os blocos simultaneamente. Evite misturar versões de blocos
no seu app. Embora os componentes do Material 3 estejam em um artefato
separado (
androidx.wear.protolayout:protolayout-material3), o que torna tecnicamente possível usar blocos do M2.5 e do M3 no mesmo app, não recomendamos essa abordagem, a menos que seja absolutamente necessário (por exemplo, se o app tiver um grande número de blocos que não podem ser migrados de uma só vez). - Adote a orientação de UX dos blocos. Devido à natureza altamente estruturada e baseada em modelos dos blocos, use os designs nos exemplos atuais como pontos de partida para seus próprios designs.
- Teste em vários tamanhos de tela e de fonte. Os blocos geralmente são densos em informações, o que torna o texto (especialmente quando colocado em botões) suscetível a estouro e corte. Para minimizar isso, use os componentes pré-criados e evite personalizações extensivas. Teste usando o recurso de visualização de blocos do Android Studio e em vários dispositivos reais.
Processo de migração
Para migrar seus blocos, siga estas etapas:
Atualizar dependências
Primeiro, atualize o arquivo build.gradle.kts. Atualize as versões e mude a
dependência protolayout-material para protolayout-material3, conforme mostrado:
// In build.gradle.kts
//val tilesVersion = "1.4.1"
//val protoLayoutVersion = "1.2.1"
// Use these versions for M3.
val tilesVersion = "1.5.0"
val protoLayoutVersion = "1.3.0"
dependencies {
// Use to implement support for wear tiles
implementation("androidx.wear.tiles:tiles:$tilesVersion")
// Use to utilize standard components and layouts in your tiles
implementation("androidx.wear.protolayout:protolayout:$protoLayoutVersion")
// Use to utilize components and layouts with Material Design in your tiles
// implementation("androidx.wear.protolayout:protolayout-material:$protoLayoutVersion")
implementation("androidx.wear.protolayout:protolayout-material3:$protoLayoutVersion")
// Use to include dynamic expressions in your tiles
implementation("androidx.wear.protolayout:protolayout-expression:$protoLayoutVersion")
// Use to preview wear tiles in your own app
debugImplementation("androidx.wear.tiles:tiles-renderer:$tilesVersion")
// Use to fetch tiles from a tile provider in your tests
testImplementation("androidx.wear.tiles:tiles-testing:$tilesVersion")
}
O TileService permanece praticamente inalterado
As principais mudanças nessa migração afetam os componentes da interface. Consequentemente, sua implementação de TileService, incluindo mecanismos de carregamento de recursos, precisa de poucas ou nenhuma modificação.
A principal exceção envolve o rastreamento de atividades de bloco: se o app usa
onTileEnterEvent() ou onTileLeaveEvent(), recomendamos migrar para
onRecentInteractionEventsAsync(). A partir da API 36, esses eventos serão agrupados.
Adaptar o código de geração de layout
No ProtoLayout 1.2 (M2.5), o método onTileRequest() retorna um TileBuilders.Tile. Esse objeto continha vários elementos, incluindo um
TimelineBuilders.Timeline, que, por sua vez, continha o LayoutElement
descrevendo a interface do bloco.
Com o ProtoLayout 1.3 (M3), embora a estrutura e o fluxo de dados gerais não tenham mudado, o LayoutElement agora é construído usando uma abordagem inspirada no Compose com um layout baseado em slots definidos, que são (de cima para baixo) o titleSlot (opcional; geralmente para um título ou cabeçalho principal), o mainSlot (obrigatório; para o conteúdo principal) e o bottomSlot (opcional; geralmente para ações como um botão de borda ou informações complementares, como um texto curto). Esse layout é construído pela função primaryLayout().
Comparação entre as funções de layout do M2.5 e do M3
M2.5
fun myLayout(
context: Context,
deviceConfiguration: DeviceParametersBuilders.DeviceParameters
) =
PrimaryLayout.Builder(deviceConfiguration)
.setResponsiveContentInsetEnabled(true)
.setContent(
Text.Builder(context, "Hello World!")
.setTypography(Typography.TYPOGRAPHY_BODY1)
.setColor(argb(0xFFFFFFFF.toInt()))
.build()
)
.build()
M3
fun myLayout(
context: Context,
deviceConfiguration: DeviceParametersBuilders.DeviceParameters,
) =
materialScope(context, deviceConfiguration) {
primaryLayout(mainSlot = { text("Hello, World!".layoutString) })
}
Para destacar as principais diferenças:
- Eliminação de builders. O padrão builder anterior para componentes da interface do Material foi substituído por uma sintaxe mais declarativa, inspirada no Compose. Componentes não relacionados à interface, como String/Color/Modifiers, também recebem novos wrappers do Kotlin.
- Funções de inicialização e layout padronizadas. Os layouts do M3 dependem de funções de inicialização e estrutura padronizadas:
materialScope()eprimaryLayout(). Essas funções obrigatórias inicializam o ambiente M3 (temas, escopo de componentes usandomaterialScope) e definem o layout principal baseado em slots (usandoprimaryLayout). Ambas precisam ser chamadas exatamente uma vez por layout.
Temas
O Material 3 apresenta várias mudanças no tema, incluindo cores dinâmicas e um conjunto expandido de opções de tipografia e forma.
Cor
Um recurso de destaque do Material 3 Expressive é a "tematização dinâmica": os blocos que ativam esse recurso (ativado por padrão) são mostrados no tema fornecido pelo sistema (a disponibilidade depende do dispositivo e da configuração do usuário).
Outra mudança no M3 é a expansão do número de tokens de cor, que aumentou de 4 para 29. Os novos tokens de cor podem ser encontrados na classe
ColorScheme.
Tipografia
Assim como o M2.5, o M3 depende muito de constantes predefinidas de tamanho da fonte. Não é recomendável especificar um tamanho de fonte diretamente. Essas constantes estão localizadas na classe
Typography e oferecem um intervalo um pouco maior de opções
mais expressivas.
Para conferir todos os detalhes, consulte a documentação de tipografia.
Forma
A maioria dos componentes do M3 pode variar de acordo com a dimensão da forma e da cor.
Um textButton (em mainSlot) com a forma full:
O mesmo textButton com forma small:
Componentes
Os componentes do M3 são mais flexíveis e configuráveis do que os equivalentes do M2.5. O M2.5 geralmente exigia componentes distintos para vários tratamentos visuais, enquanto o M3 frequentemente usa um componente de base generalizado e altamente configurável com bons padrões.
Esse princípio também se aplica ao layout raiz. No M2.5, isso era um
PrimaryLayout ou um EdgeContentLayout. No M3, depois de
estabelecer um único MaterialScope de nível superior, chame a função
primaryLayout(). Essa função retorna o layout raiz
diretamente (sem necessidade de builders) e aceita LayoutElements para vários slots, como
titleSlot, mainSlot e bottomSlot. É possível preencher esses slots com
componentes de UI concretos, como os retornados por text(),
button() ou card(), ou com estruturas de layout, como Row ou
Column de LayoutElementBuilders.
Os temas são outra melhoria importante do M3. Por padrão, os elementos da interface seguem automaticamente as especificações de estilo do M3 e são compatíveis com temas dinâmicos.
| M2.5 | M3 |
|---|---|
| Elementos interativos | |
Button ou Chip |
|
| Texto | |
Text |
text() |
| Indicadores de progresso | |
CircularProgressIndicator |
circularProgressIndicator() ou segmentedCircularProgressIndicator() |
| Layout | |
PrimaryLayout ou EdgeContentLayout |
primaryLayout() |
| — | buttonGroup() |
| Imagens | |
| — | icon(), avatarImage() ou backgroundImage() |
Modificadores
No M3, Modifiers, que você usa para decorar ou aumentar um componente, são
mais parecidos com o Compose. Essa mudança pode reduzir o boilerplate construindo automaticamente os tipos internos apropriados. Essa mudança é independente do uso de componentes de interface do M3. Se necessário, você pode usar modificadores de estilo builder do ProtoLayout 1.2 com componentes de interface do M3 e vice-versa.
M2.5
// A Builder-style modifier to set the opacity of an element to 0.5
fun myModifier(): ModifiersBuilders.Modifiers =
ModifiersBuilders.Modifiers.Builder()
.setOpacity(TypeBuilders.FloatProp.Builder(0.5F).build())
.build()
M3
// The equivalent Compose-like modifier is much simpler
fun myModifier(): LayoutModifier = LayoutModifier.opacity(0.5F)
É possível construir modificadores usando qualquer estilo de API, e também é possível usar a função de extensão toProtoLayoutModifiers() para converter um LayoutModifier em um ModifiersBuilders.Modifier.
Funções auxiliares
Embora o ProtoLayout 1.3 permita que muitos componentes de UI sejam expressos usando uma
API inspirada no Compose, elementos de layout fundamentais, como linhas e
colunas de LayoutElementBuilders, continuam usando o padrão
builder. Para diminuir essa diferença de estilo e promover a consistência com as novas APIs de componentes do M3, use funções auxiliares.
Sem Helpers
primaryLayout(
mainSlot = {
LayoutElementBuilders.Column.Builder()
.setWidth(expand())
.setHeight(expand())
.addContent(text("A".layoutString))
.addContent(text("B".layoutString))
.addContent(text("C".layoutString))
.build()
}
)
Com ajudantes
// Function literal with receiver helper function
fun column(builder: Column.Builder.() -> Unit) =
Column.Builder().apply(builder).build()
primaryLayout(
mainSlot = {
column {
setWidth(expand())
setHeight(expand())
addContent(text("A".layoutString))
addContent(text("B".layoutString))
addContent(text("C".layoutString))
}
}
)
Migrar para o Tiles 1.2 / ProtoLayout 1.0
Na versão 1.2 e mais recentes, a maioria das APIs de layout de blocos está no namespace
androidx.wear.protolayout. Para usar as APIs mais recentes, siga as etapas de migração abaixo no seu código.
Atualizar dependências
No arquivo de build do módulo do app, faça estas mudanças:
Groovy
// Removeimplementation 'androidx.wear.tiles:tiles-material:version'// Include additional dependencies implementation "androidx.wear.protolayout:protolayout:1.3.0" implementation "androidx.wear.protolayout:protolayout-material:1.3.0" implementation "androidx.wear.protolayout:protolayout-expression:1.3.0" // Update implementation "androidx.wear.tiles:tiles:1.5.0"
Kotlin
// Removeimplementation("androidx.wear.tiles:tiles-material:version")// Include additional dependencies implementation("androidx.wear.protolayout:protolayout:1.3.0") implementation("androidx.wear.protolayout:protolayout-material:1.3.0") implementation("androidx.wear.protolayout:protolayout-expression:1.3.0") // Update implementation("androidx.wear.tiles:tiles:1.5.0")
Atualizar namespaces
Nos arquivos de código com base em Kotlin e Java do seu app, faça estas mudanças: Como alternativa, execute este script de renomeação de namespace.
- Substitua todas as importações
androidx.wear.tiles.material.*porandroidx.wear.protolayout.material.*. Conclua essa etapa para a bibliotecaandroidx.wear.tiles.material.layoutstambém. Substitua a maioria das outras importações
androidx.wear.tiles.*porandroidx.wear.protolayout.*.As importações para
androidx.wear.tiles.EventBuilders,androidx.wear.tiles.RequestBuilders,androidx.wear.tiles.TileBuilderseandroidx.wear.tiles.TileServiceprecisam ser as mesmas.Renomeie alguns métodos descontinuados das classes TileService e TileBuilder:
TileBuilders:getTimeline()paragetTileTimeline()esetTimeline()parasetTileTimeline()TileService:onResourcesRequest()paraonTileResourcesRequest()RequestBuilders.TileRequest:getDeviceParameters()paragetDeviceConfiguration(),setDeviceParameters()parasetDeviceConfiguration(),getState()paragetCurrentState()esetState()parasetCurrentState()