Em dispositivos Wear OS, os blocos são renderizados por dois componentes principais com versão independente. Para garantir que os blocos de apps funcionem corretamente em todos os dispositivos, é importante entender essa arquitetura.
- Bibliotecas relacionadas a blocos do Jetpack: essas bibliotecas (incluindo os blocos do Wear
e o Wear ProtoLayout) são incorporadas ao 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 seu Bloco) em resposta à chamadaonTileRequest()do sistema. - ProtoLayout Renderer: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 pode variar com base nas versões da biblioteca Jetpack Tiles do app e na versão do ProtoLayout Renderer 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 garantir que seu app seja compatível com diferentes versões da biblioteca Tiles e do ProtoLayout Renderer e como migrar para versões mais recentes da biblioteca do Jetpack.
Considere a compatibilidade
Para criar um Bloco que funcione corretamente em vários dispositivos, considere o seguinte:
Detectar a versão do renderizador
- Use o método
getRendererSchemaVersion()do objetoDeviceParameterstransmitido para o método onTileRequest(). 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 de
onTileRequest()para adaptar o design ou o comportamento do Bloco com base na versão do renderizador detectada.- Por exemplo, se uma animação específica não tiver suporte, você poderá mostrar uma imagem estática.
A anotação @RequiresSchemaVersion
- A anotação
@RequiresSchemaVersionem métodos ProtoLayout indica a versão mínima do esquema do renderizador necessária para que esse método se comporte como documentado (exemplo).- Chamar um método que exige uma versão mais recente do renderizador do que está disponível no dispositivo não faz com que o app falhe, mas pode fazer com que o conteúdo não seja exibido ou o recurso seja ignorado.
Exemplo
override fun onTileRequest(
requestParams: TileService.TileRequest
): ListenableFuture<Tile> {
val rendererVersion =
requestParams.deviceConfiguration.rendererSchemaVersion
val tile = Tile.Builder()
if (
rendererVersion.major > 1 ||
(rendererVersion.major == 1 && rendererVersion.minor >= 300)
) {
// Use a feature supported in renderer version 1.300 or later
tile.setTileTimeline(/* ... */ )
} else {
// Provide fallback content for older renderers
tile.setTileTimeline(/* ... */ )
}
return Futures.immediateFuture(tile.build())
}
Testar com diferentes versões do renderizador
Para testar os 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 entregues 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 visualização de blocos do Android Studio usa um renderizador incorporado na biblioteca Jetpack ProtoLayout da qual seu código depende. Portanto, outra abordagem é depender de diferentes versões da biblioteca do Jetpack ao testar blocos.
Migrar para o Tiles 1.5 / ProtoLayout 1.3 (Material 3 Expressivo)
Atualize as bibliotecas de blocos do Jetpack para aproveitar os aprimoramentos mais recentes, incluindo mudanças na interface para integrar os blocos ao sistema.
O Jetpack Tiles 1.5 e o Jetpack ProtoLayout 1.3 apresentam várias melhorias e mudanças importantes. São eles:
- Uma API semelhante ao Compose para descrever a interface.
- Componentes Expressivo do Material 3, 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
- Migrar todos os blocos ao mesmo tempo. Evite misturar versões de blocos
no app. Embora os componentes do Material 3 residam em um artefato
separado (
androidx.wear.protolayout:protolayout-material3), o que torna tecnicamente possível usar os blocos M2.5 e M3 no mesmo app, recomendamos fortemente 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). - Adotar a orientação de UX dos Blocos. Dada a natureza altamente estruturada e de modelo dos blocos, use os designs nas amostras atuais como pontos de partida para seus próprios designs.
- Teste em vários tamanhos de tela e fontes. Os blocos geralmente têm muitas informações, o que faz com que o texto (especialmente quando colocado em botões) seja suscetível a transbordamento e recorte. Para minimizar isso, use os componentes pré-criados e evite personalizações extensas. Teste usando o recurso de visualização de blocos do Android Studio e em vários dispositivos reais.
Processo de migração
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-rc01"
val protoLayoutVersion = "1.3.0-rc01"
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,
a 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 blocos: se o app usa
onTileEnterEvent() ou onTileLeaveEvent(), migre 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
que descreve a interface do bloco.
Com o ProtoLayout 1.3 (M3), embora a estrutura e o fluxo de dados em geral 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)
titleSlot (opcional; normalmente para um título ou cabeçalho principal), mainSlot
(obrigatório; para o conteúdo principal) e bottomSlot (opcional; geralmente para ações
como um botão de borda ou informações complementares, como texto curto). Esse layout é
construído pela função primaryLayout().
Comparação das funções de layout M2.5 e 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 de builder tradicional para componentes da interface do Material3 é substituído por uma sintaxe mais declarativa inspirada no Compose. Os componentes que não são da 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 (tema, escopo do componente pelomaterialScope) e definem o layout principal baseado em slot (peloprimaryLayout). Ambas precisam ser chamadas exatamente uma vez por layout.
Temas
Cor
Um recurso de destaque do Material 3 Expressivo é a "aplicação de temas dinâmicos": os blocos que ativam esse recurso (ativado por padrão) serã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 de tamanho de fonte predefinidas. Não é recomendável
especificar diretamente um tamanho de fonte. Essas constantes estão localizadas na classe
Typography e oferecem uma gama um pouco mais ampla de opções
mais expressivas.
Para conferir todos os detalhes, consulte a documentação sobre tipografia.
Forma
A maioria dos componentes do M3 pode variar de acordo com a dimensão da forma e da cor.
Um textButton (no mainSlot) com a forma full:
O mesmo botão de texto com a forma small:
Componentes
Os componentes do M3 são muito mais flexíveis e configuráveis do que os equivalentes do M2.5. Enquanto a M2.5 geralmente exigia componentes distintos para tratamentos visuais variados, a M3 emprega com frequência um componente "base" generalizado, mas altamente configurável, com bons padrões.
Esse princípio se aplica ao layout "raiz". No M2.5, era um
PrimaryLayout ou um EdgeContentLayout. No M3, depois que uma única
MaterialScope de nível superior é estabelecida, a função
primaryLayout() é chamada. Isso retorna o layout raiz diretamente (sem necessidade de builders) e
aceita LayoutElements para vários "slots", como titleSlot, mainSlot
e bottomSlot. Esses slots podem ser preenchidos com componentes de interface concretos, como
os retornados por text(), button() ou card(), ou 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 aderem automaticamente às especificações de estilo do M3 e oferecem suporte a 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, o Modifiers, que você usa para decorar ou aumentar um componente, é
mais parecido com o Compose. Essa mudança pode reduzir o modelo padrão ao construir automaticamente
os tipos internos apropriados. Essa mudança é ortogonal ao
uso de componentes de interface do M3. Se necessário, use modificadores no estilo de 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 criar modificadores usando qualquer estilo de API e também 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 da interface sejam expressos usando uma
API inspirada no Compose, elementos de layout fundamentais, como linhas e
colunas do LayoutElementBuilders, continuam usando o padrão
do builder. Para preencher essa lacuna estilística e promover a consistência com as novas APIs de
componentes do M3, use funções auxiliares.
Sem auxiliares
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 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()