Com as visualizações do Compose no Android Studio, é possível inspecionar e verificar os elementos combináveis do Wear OS em diferentes tamanhos de tela de relógio, molduras redondas e escalas de fonte diretamente no ambiente de desenvolvimento integrado, sem implantar o app em um relógio físico ou emulador.
Como os dispositivos Wear OS têm telas circulares em que os cantos cortam o conteúdo e as sobreposições do sistema, como TimeText e ScrollIndicator, se curvam ao longo da borda da tela, é essencial configurar prévias especificamente para o Wear OS para detectar problemas de layout no início.
Configurar dependências de visualização
Para usar as anotações de visualização do Compose para Wear OS e as definições de dispositivo, adicione as dependências abaixo ao arquivo build.gradle.kts do módulo:
dependencies {
// Provides @WearPreview* multipreview annotations
// (such as @WearPreviewDevices and @WearPreviewFontScales)
implementation("androidx.wear.compose:compose-ui-tooling:1.7.0")
// Provides WearDevices constants
// (such as WearDevices.SMALL_ROUND and WearDevices.LARGE_ROUND)
implementation("androidx.wear:wear-tooling-preview:1.0.0")
// Standard Compose preview support and interactive/animation inspection
implementation("androidx.compose.ui:ui-tooling-preview")
debugImplementation("androidx.compose.ui:ui-tooling")
}
Escolher o que visualizar: telas ou componentes
A configuração de uma prévia depende de você estar visualizando uma tela cheia ou um componente de interface isolado.
Visualizar tela cheia (AppScaffold + ScreenScaffold)
Ao visualizar uma tela inteira, sempre encapsule o elemento combinável da tela em AppScaffold e ScreenScaffold usando uma anotação de visualização do dispositivo Wear. Isso renderiza a tela circular do relógio e garante que:
TimeTexté renderizado na borda curva superior do mostrador do relógio.- O
ScrollIndicatoraparece ao longo da borda direita. EdgeButtonestá posicionado corretamente e cortado na curva de baixo.- O preenchimento de conteúdo e o corte circular da tela refletem com precisão o hardware de relógio real.
@WearPreviewDevices @Composable fun WorkoutScreenPreview() { MaterialTheme { // AppScaffold provides the top-level TimeText overlay AppScaffold { // WorkoutScreen contains its own ScreenScaffold and content WorkoutScreen( heartRate = 142, elapsedTime = "12:45" ) } } }
Pequena redonda (192 x 192 dp)
Grande redondo (227 x 227 dp)
Visualizar componentes isolados
Ao visualizar componentes individuais, como um Card, Button ou
ícone de status personalizado, omita o parâmetro device e use um @Preview padrão com um
fundo escuro. Isso garante que as cores e o contraste do Wear Material 3 apareçam
com precisão sem renderizar um mostrador de relógio circular completo:
@Preview( showBackground = true, backgroundColor = 0xFF000000 ) @Composable fun HeartRateCardPreview() { MaterialTheme { HeartRateCard(bpm = 142, zone = "Aerobic") } }
Prévia isolada do componente (sem frame do dispositivo).
Anotações de várias visualizações integradas
O pacote androidx.wear.compose.ui.tooling.preview oferece anotações integradas que configuram automaticamente planos de fundo escuros (backgroundColor = 0xFF000000, showBackground = true) e dimensões circulares de dispositivos de relógio:
| Nota | O que ele renderiza | Quando usar |
|---|---|---|
@WearPreviewSmallRound |
1 visualização em WearDevices.SMALL_ROUND (192 x 192 dp). |
Iteração rápida no tamanho de exibição circular mais restrito. |
@WearPreviewLargeRound |
1 visualização em WearDevices.LARGE_ROUND (227 x 227 dp). |
Como inspecionar a densidade do layout e o espaçamento extra em relógios maiores. |
@WearPreviewDevices |
Duas prévias: SMALL_ROUND e LARGE_ROUND. |
Verificação padrão de vários dispositivos para cada elemento combinável de tela. |
@WearPreviewFontScales |
Seis prévias em SMALL_ROUND em todas as escalas de fonte do Wear: pequena (0.94f), normal (1.0f), média (1.06f), grande (1.12f), maior (1.18f) e muito grande (1.24f). |
Verificando ajuste de texto, elipse e expansão da altura do botão. |
É possível empilhar @WearPreviewDevices e @WearPreviewFontScales na mesma
função de visualização para gerar uma matriz de teste abrangente:
@WearPreviewDevices @WearPreviewFontScales @Composable fun MessageDetailScreenPreview() { MaterialTheme { AppScaffold { MessageDetailScreen( sender = "Alex", body = "Running 5 mins late!" ) } } }
Anotações de visualização personalizada e especificações de hardware
Quando você precisa de um controle mais refinado, como testar dimensões de hardware específicas, strings longas
localizadas ou combinações de pior caso, é possível configurar @Preview
diretamente ou definir suas próprias anotações de multipreview personalizadas.
Constantes WearDevices disponíveis e especificações de hardware personalizadas
O objeto androidx.wear.tooling.preview.devices.WearDevices fornece IDs de dispositivo padrão:
WearDevices.SMALL_ROUND("id:wearos_small_round", 192x192dp)WearDevices.LARGE_ROUND("id:wearos_large_round", 227x227dp)
Para visualizar em telas redondas extragrandes (como relógios de 44 mm a 45 mm ou modelos Ultra em 240 x 240 dp), transmita uma string spec: personalizada ao parâmetro device:
@Preview( name = "XL Round Watch (240dp)", device = "spec:width=240dp,height=240dp,dpi=320,isRound=true", showBackground = true, backgroundColor = 0xFF000000 ) @Composable fun WorkoutScreenXlPreview() { MaterialTheme { AppScaffold { WorkoutScreen(heartRate = 142, elapsedTime = "12:45") } } }
Criar uma anotação de visualização múltipla personalizada
Para inspecionar um cenário extremo, crie uma anotação de várias visualizações personalizadas que pareie a menor tela redonda com a maior escala de fonte e uma localidade detalhada (como alemão) ao lado de uma tela redonda grande padrão:
@Preview( name = "1. Standard Large Round", group = "Layout extremes", device = WearDevices.LARGE_ROUND, backgroundColor = 0xFF000000, showBackground = true ) @Preview( name = "2. Extreme Small Round (Largest Font + German)", group = "Layout extremes", device = WearDevices.SMALL_ROUND, fontScale = 1.24f, locale = "de-rDE", backgroundColor = 0xFF000000, showBackground = true ) annotation class WearPreviewExtremes
1. Redonda grande padrão
2. Redonda extremamente pequena (fonte maior + alemão)
Visualizar colunas de rolagem (TransformingLazyColumn)
Por padrão, um TransformingLazyColumn é inicializado com o primeiro item (index = 0) fixado na parte de cima da tela. No entanto, no Wear OS, os itens
mudam de altura e de cantos arredondados (SurfaceTransformation) à medida que se
aproximam das bordas curvas de cima e de baixo da tela, e o EdgeButton
só aparece quando a tela é rolada para baixo.
Para ver como sua lista aparece quando é rolada até a metade ou até a parte de baixo:
Etapa 1: elevar TransformingLazyColumnState no elemento combinável da tela
Permita que o elemento combinável da tela aceite um parâmetro TransformingLazyColumnState
com rememberTransformingLazyColumnState() como valor padrão:
@Composable fun InboxScreen( messages: List<Message>, columnState: TransformingLazyColumnState = rememberTransformingLazyColumnState(), ) { val transformationSpec = rememberTransformationSpec() ScreenScaffold( scrollState = columnState, edgeButton = { EdgeButton(onClick = { /* Compose new */ }) { Text("New message") } } ) { contentPadding -> TransformingLazyColumn( state = columnState, contentPadding = contentPadding, ) { items(messages.size) { index -> Card( onClick = {}, modifier = Modifier .fillMaxWidth() .transformedHeight(this, transformationSpec) .minimumVerticalContentPadding( CardDefaults.minimumVerticalListContentPadding ), transformation = SurfaceTransformation(transformationSpec), ) { Text(messages[index].subject) } } } } }
Etapa 2: transmitir initialAnchorItemIndex no seu @Preview
rememberTransformingLazyColumnState aceita dois parâmetros opcionais de rolagem inicial:
initialAnchorItemIndex: Int: quando definido como um índice não negativo (por exemplo,3), a lista é inicializada com esse item centralizado na janela de visualização do relógio.initialAnchorItemScrollOffset: Int: ajuste de pixel opcional aplicado em relação ao item de âncora centralizado.
É possível criar prévias lado a lado mostrando os estados Superior, Meio (rolado) e Inferior (EdgeButton visível) da mesma tela:
@WearPreviewLargeRound @Composable fun InboxScreenTopPreview() { MaterialTheme { AppScaffold { // Default (-1): Pinned to top of list (index 0) InboxScreen(messages = sampleMessages) } } } @WearPreviewLargeRound @Composable fun InboxScreenScrolledMiddlePreview() { MaterialTheme { AppScaffold { // Centers item index 3 in the viewport, showing top/bottom item morphing InboxScreen( messages = sampleMessages, columnState = rememberTransformingLazyColumnState( initialAnchorItemIndex = 3 ) ) } } } @WearPreviewLargeRound @Composable fun InboxScreenBottomEdgeButtonPreview() { MaterialTheme { AppScaffold { // Anchors on the last item so the EdgeButton is visible at the bottom InboxScreen( messages = sampleMessages, columnState = rememberTransformingLazyColumnState( initialAnchorItemIndex = sampleMessages.lastIndex ) ) } } }
Comando "top" (padrão -1)
Médio (initialAnchorItemIndex = 3)
Parte de baixo (EdgeButton expandido)
Dica:também é possível clicar em Iniciar modo interativo em qualquer
@Previewno Android Studio para rolar oTransformingLazyColumnao vivo com o mouse ou trackpad e inspecionar a transformaçãoSurfaceTransformation, as animações de entradaEdgeButtone o movimentoScrollIndicatorem tempo real.
Proteção ScrollIndicator durante a captura de rolagem (LocalScrollCaptureInProgress)
Quando a captura de tela rolável do sistema (capturas de tela longas) ou ferramentas de teste de captura de tela de vários frames
capturam uma rolagem TransformingLazyColumn, o Compose define
LocalScrollCaptureInProgress.current como true ao capturar e unir
vários blocos de viewport verticalmente.
Como o ScreenScaffold não oculta automaticamente o scrollIndicator durante a captura de rolagem, a sobreposição da barra de rolagem flutuante aparece repetida em todos os blocos combinados de uma captura de tela longa, a menos que você a proteja explicitamente com !LocalScrollCaptureInProgress.current:
ScreenScaffold( scrollState = columnState, scrollIndicator = { if (!LocalScrollCaptureInProgress.current) { ScrollIndicator(state = columnState) } } ) { contentPadding -> // TransformingLazyColumn content... // ... }