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"

    // Use to utilize standard components and layouts in your tiles
    implementation "androidx.wear.protolayout:protolayout:1.3.0"

    // Use to utilize components and layouts with Material Design in your tiles
    implementation "androidx.wear.protolayout:protolayout-material:1.3.0"

    // Use to include dynamic expressions in your tiles
    implementation "androidx.wear.protolayout:protolayout-expression:1.3.0"

    // Use to preview wear tiles in your own app
    debugImplementation "androidx.wear.tiles:tiles-renderer:1.5.0"

    // Use to fetch tiles from a tile provider in your tests
    testImplementation "androidx.wear.tiles:tiles-testing:1.5.0"
}

Kotlin

dependencies {
    // Use to implement support for wear tiles
    implementation("androidx.wear.tiles:tiles:1.5.0")

    // Use to utilize standard components and layouts in your tiles
    implementation("androidx.wear.protolayout:protolayout:1.3.0")

    // Use to utilize components and layouts with Material Design in your tiles
    implementation("androidx.wear.protolayout:protolayout-material:1.3.0")

    // Use to include dynamic expressions in your tiles
    implementation("androidx.wear.protolayout:protolayout-expression:1.3.0")

    // Use to preview wear tiles in your own app
    debugImplementation("androidx.wear.tiles:tiles-renderer:1.5.0")

    // Use to fetch tiles from a tile provider in your tests
    testImplementation("androidx.wear.tiles:tiles-testing:1.5.0")
}

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. Isso é feito com a função primaryLayout().
  • Elementos de layout:representam um elemento gráfico individual, como um botão ou um card, ou vários elementos agrupados usando uma coluna, um buttonGroup ou 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. Você pode fornecer vários mecanismos e expressões para indicar quando o renderizador deve alternar de um objeto de layout para outro, como parar de mostrar um layout em um horário 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 vai conter o ID do botão. Também é possível trocar tipos de dados usando um mapa.
  • Bloco:um objeto TileBuilders.Tile que representa um bloco, consistindo em uma linha do tempo, um ID da versão dos 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 de gráficos usada em várias plataformas do Wear OS.

Criar um bloco

Para fornecer um bloco do seu app, implemente um serviço do tipo TileService e registre-o no manifesto. Com isso, o sistema solicita os blocos necessários durante as chamadas para onTileRequest() e os 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(
                        materialScope(this, requestParams.deviceConfiguration) {
                            primaryLayout(
                                mainSlot = {
                                    text("Hello, World!".layoutString, typography = BODY_LARGE)
                                }
                            )
                        }
                    )
                )
                .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.m3.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 os blocos no smartphone ou smartwatch. O recurso de visualização é compatível com todos os qualificadores de recursos padrão do Android. Assim, é possível variar a visualização de acordo com fatores como tamanho da tela e idioma do dispositivo. Consulte a lista de verificação da prévia para mais recomendações.

Implante o app e adicione o bloco ao carrossel de blocos. Há também uma maneira mais fácil para desenvolvedores de visualizar um bloco, mas, por enquanto, faça isso manualmente.

Bloco &quot;Hello World&quot;.
Figura 1. Bloco "Hello World".

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

Criar uma IU para blocos

Os elementos expressivos da interface do Material 3 são criados usando uma abordagem estruturada com tecnologia do padrão de builder com segurança de tipo do Kotlin.

Layout

Para orientações sobre os princípios de design por trás da criação de layouts de blocos eficazes e responsivos, consulte Layouts comuns para blocos.

Para criar seu layout, faça o seguinte:

  1. Inicie um escopo do Material Design:chame a função materialScope(), fornecendo os context e deviceConfiguration necessários. Você pode incluir parâmetros opcionais, como allowDynamicTheme e defaultColorScheme. O allowDynamicTheme é true por padrão, e o defaultColorScheme representa o ColorScheme usado quando as cores dinâmicas não estão disponíveis, como quando o usuário desativou o recurso ou quando ele não é compatível com o dispositivo ou allowDynamicTheme é false.

  2. Crie sua interface no escopo:todos os componentes de interface de um determinado layout de bloco precisam ser definidos na lambda de uma única chamada materialScope() de nível superior. Essas funções de componente, como primaryLayout() e textEdgeButton(), são funções de extensão em MaterialScope e só ficam disponíveis quando chamadas nesse escopo de receptor.

    materialScope(
        context = context,
        deviceConfiguration = requestParams.deviceConfiguration, // requestParams is passed to onTileRequest
        defaultColorScheme = myFallbackColorScheme
    ) {
        // inside the MaterialScope, you can call functions like primaryLayout()
        primaryLayout(
            titleSlot = { text(text = "Title".layoutString) },
            mainSlot = { text(text = "Main Content".layoutString) },
            bottomSlot = { textEdgeButton(text = "Action".layoutString) }
        )
    }
    

Caça-níqueis

No M3, o layout de blocos usa uma abordagem inspirada no Compose que utiliza três slots distintos. De cima para baixo, são:

  1. O titleSlot, geralmente para um título ou cabeçalho principal.
  2. O mainSlot do conteúdo principal.
  3. O bottomSlot, geralmente usado para ações ou informações complementares. É também onde um botão de borda aparece.
Layout de blocos mostrando titleSlot, mainSlot e bottomSlot
Figura 2: titleSlot, mainSlot e bottomSlot.

O conteúdo de cada slot é o seguinte:

  • titleSlot (opcional): geralmente algumas palavras geradas pelo text().
  • mainSlot (obrigatório): componentes organizados em estruturas como linhas, colunas e grupos de botões. Esses componentes também podem ser incorporados recursivamente uns dentro dos outros. Por exemplo, uma coluna pode conter linhas.
  • bottomSlot (opcional): geralmente preenchido com um botão de ajuste de borda ou um rótulo de texto.

Como os blocos não podem ser rolados, não há componentes para paginação, rolagem ou processamento de longas listas de conteúdo. Verifique se o conteúdo continua visível quando o tamanho da fonte aumenta ou o texto fica mais longo devido à tradução.

Componentes de interface

A biblioteca protolayout-material3 oferece um grande número de componentes projetados de acordo com as especificações expressivas do Material 3 e as recomendações de interface do usuário.

Botões

Os botões são principalmente orientados à ação. Eles servem para acionar ações específicas. O conteúdo de cada botão, como um ícone ou um texto curto, identifica a ação.

  • textButton(): botão com um único slot para conteúdo de texto (curto)
  • iconButton(): botão com um único slot para representar um ícone
  • avatarButton(): botão de avatar em forma de pílula que oferece até três slots para receber conteúdo representando um rótulo e um rótulo secundário empilhados verticalmente, além de uma imagem (avatar) ao lado.
  • imageButton(): botão de imagem clicável que não oferece slots adicionais, apenas imagem (por exemplo, backgroundImage como plano de fundo).
  • compactButton(): botão compacto que oferece até dois slots para usar conteúdo empilhado horizontalmente representando um ícone e um texto ao lado dele.
  • button(): botão em formato de pílula que oferece até três slots para receber conteúdo representando um rótulo e um rótulo secundário empilhados verticalmente, além de um ícone ao lado.

Botões de borda

Um botão de borda é um botão especializado de largura total ancorado na parte de baixo da tela redonda de um relógio, representando a ação mais importante a ser realizada na tela de bloco atual.

  • iconEdgeButton(): botão de borda que oferece um único slot para um ícone ou conteúdo pequeno e redondo semelhante.
  • textEdgeButton(): botão de borda que oferece um único slot para um texto ou conteúdo semelhante em comprimento e largura.

Cartões

Os cards são principalmente orientados a informações. Elas mostram coleções de dados estruturados relacionados. Embora seja possível criar cards interativos, eles geralmente mostram um resumo das informações, que o usuário pode tocar para ver mais detalhes ou realizar uma ação relacionada.

  • titleCard(): cartão de título que oferece de um a três slots, geralmente com base em texto.
  • appCard(): card do app que oferece até cinco espaços, geralmente baseados em texto.
  • textDataCard(): cartão de dados que oferece até três slots empilhados verticalmente, geralmente com base em texto ou números.
  • iconDataCard(): cartão de dados que oferece até três slots empilhados verticalmente, geralmente baseados em texto ou numeral, com um ícone.
  • graphicDataCard(): card de dados gráficos que oferece um slot para dados gráficos, como indicador de progresso, e até dois slots empilhados verticalmente, geralmente para descrições de texto.

Indicadores de progresso

Agrupamento de elementos de layout

  • buttonGroup(): layout de componente que coloca os filhos em uma sequência horizontal
  • primaryLayout(): layout de tela cheia que representa um estilo de layout M3 sugerido, responsivo e que cuida do posicionamento dos elementos, além das margens e do padding recomendados.

Temas

No Material 3 Expressive, o sistema de cores é definido por 29 funções de cores padrão, organizadas em seis grupos: primária, secundária, terciária, erro, superfície e contorno.

Sistema de cores expressivas do Material 3
Figura 3. O sistema de cores expressivas do Material 3.

Um ColorScheme mapeia cada uma dessas 29 funções para uma cor correspondente. Como ele faz parte do MaterialScope e os componentes precisam ser criados nele, eles recebem automaticamente as cores do esquema. Essa abordagem permite que todos os elementos da interface do usuário sigam automaticamente os padrões do Material Design.

Para permitir que os usuários escolham entre um esquema de cores definido por você, como um que reflita as cores da sua marca, e um fornecido pelo sistema, derivado do mostrador do relógio atual do usuário ou escolhido por ele, inicialize o MaterialScope da seguinte maneira:

val myColorScheme =
    ColorScheme(
        primary = ...
        onPrimary = ...
        // 27 more
    )

materialScope(
  defaultColorScheme = myColorScheme
) {
  // If the user selects "no theme" in settings, myColorScheme is used.
  // Otherwise, the system-provided theme is used.
}

Para forçar a exibição dos blocos no esquema de cores fornecido, desative o suporte a temas dinâmicos definindo allowDynamicTheme como false:

materialScope(
  allowDynamicTheme = false,
  defaultColorScheme = myColorScheme
) {
  // myColorScheme is *always* used.
}

Cor

Cada componente individual usa um subconjunto das 29 funções de cores definidas por uma ColorScheme. Por exemplo, os botões usam até quatro cores, que por padrão são extraídas do grupo "primário" do ColorScheme ativo:

Token de componente ButtonColors Papel ColorScheme
containerColor principal
iconColor onPrimary
labelColor onPrimary
secondaryLabelColor onPrimary (opacidade 0,8)

Para orientações detalhadas sobre como aplicar cores aos designs do Wear OS, consulte o guia de design de cores.

Talvez seja necessário desviar dos tokens de cores padrão para elementos específicos da interface. Por exemplo, talvez você queira que um textEdgeButton use cores do grupo "secundário" ou "terciário", em vez de "primário", para se destacar e dar um contraste melhor.

É possível personalizar as cores dos componentes de várias maneiras:

  1. Use uma função auxiliar para cores predefinidas. Use funções auxiliares como filledTonalButtonColors() para aplicar os estilos de botão padrão do Material 3 Expressive. Essas funções criam instâncias ButtonColors pré-configuradas que mapeiam estilos comuns, como preenchido, tonal ou delineado, para as funções apropriadas do ColorScheme ativo no MaterialScope. Isso permite aplicar estilos consistentes sem definir manualmente cada cor para tipos de botões comuns.

    textEdgeButton(
        colors = filledButtonColors() // default
        /* OR colors = filledTonalButtonColors() */
        /* OR colors = filledVariantButtonColors() */
        // ... other parameters
    )
    

    Para cartões, use a família equivalente filledCardColors() de funções.

    Também é possível modificar o objeto ButtonColors retornado por funções auxiliares usando o método copy() se você precisar mudar apenas um ou dois tokens:

    textEdgeButton(
        colors =
            filledButtonColors()
                .copy(
                    containerColor = colorScheme.tertiary,
                    labelColor = colorScheme.onTertiary
                )
        // ... other parameters
    )
    
  2. Forneça explicitamente papéis de cores de substituição. Crie seu próprio objeto ButtonColors e transmita-o ao componente. Para cards, use o objeto CardColors equivalente.

    textEdgeButton(
        colors =
            ButtonColors(
                // the materialScope makes colorScheme available
                containerColor = colorScheme.secondary,
                iconColor = colorScheme.secondaryDim,
                labelColor = colorScheme.onSecondary,
                secondaryLabelColor = colorScheme.onSecondary
            )
        // ... other parameters
    )
    
  3. Especifique cores fixas (use com cuidado). Embora geralmente seja recomendável especificar cores pela função semântica delas (por exemplo, colorScheme.primary), também é possível fornecer valores de cores diretos. Essa abordagem deve ser usada com moderação, já que pode levar a inconsistências com o tema geral, principalmente se ele mudar de forma dinâmica.

    textEdgeButton(
        colors = filledButtonColors().copy(
            containerColor = android.graphics.Color.RED.argb, // Using named colors
            labelColor = 0xFFFFFF00.argb // Using a hex code for yellow
        )
        // ... other parameters
    )
    

Tipografia

Para mais informações sobre como usar a tipografia de maneira eficaz nos seus designs, consulte o guia de design de tipografia.

Para criar consistência visual na plataforma Wear OS e otimizar o desempenho, todo o texto nos blocos é renderizado usando uma fonte fornecida pelo sistema. Ou seja, os blocos não são compatíveis com famílias tipográficas personalizadas. No Wear OS 6 e versões mais recentes, essa é uma fonte específica do OEM. Na maioria dos casos, será uma fonte variável, oferecendo uma experiência mais expressiva e um controle mais granular.

Para criar um estilo de texto, geralmente você usa o método text() combinado com constantes tipográficas. Com esse componente, você pode usar funções de tipografia predefinidas no Material 3 Expressive, o que ajuda seu bloco a seguir as práticas recomendadas de tipografia estabelecidas para legibilidade e hierarquia. A biblioteca oferece um conjunto de 18 constantes de tipografia semântica, como BODY_MEDIUM. Essas constantes também afetam outros eixos de fontes além do tamanho.

text(
    text = "Hello, World!".layoutString,
    typography = BODY_MEDIUM,
)

Para ter mais controle, você pode fornecer outras configurações. No Wear OS 6 e em versões mais recentes, provavelmente será usada uma fonte variável, que pode ser modificada ao longo dos eixos itálico, espessura, largura e arredondamento. É possível controlar esses eixos usando o parâmetro settings:

text(
    text = "Hello, World".layoutString,
    italic = true,

    // Use elements defined in androidx.wear.protolayout.LayoutElementBuilders.FontSetting
    settings =
        listOf(weight(500), width(100F), roundness(100)),
)

Por fim, se você precisar controlar o tamanho ou o espaçamento entre letras (não recomendado), use basicText() em vez de text() e crie um valor para a propriedade fontStyle usando fontStyle().

Formato e margens

É possível mudar o raio do canto de quase todos os componentes usando a propriedade shape. Os valores vêm da propriedade MaterialScope shapes:

textButton(
   height = expand(),
   width = expand(),
   shape = shapes.medium, // OR another value like shapes.full
   colors = filledVariantButtonColors(),
   labelContent = { text("Hello, World!".layoutString) },
)

Depois de alterar o formato de um componente, se você achar que ele deixa muito ou pouco espaço ao redor da borda da tela, ajuste as margens usando o parâmetro margin de primaryLayout():

primaryLayout(
    mainSlot = {
        textButton(
            shape = shapes.small,
            /* ... */
        )
    },
    // margin constants defined in androidx.wear.protolayout.material3.PrimaryLayoutMargins
    margins = MAX_PRIMARY_LAYOUT_MARGIN,
)

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

Lista de verificação de imagens de visualização de bloco

O sistema mostra a imagem de visualização do bloco, referenciada no manifesto do app Android, no editor de carrossel de blocos para adicionar um novo bloco. Esse editor aparece em dispositivos Wear OS e no app complementar do relógio em smartphones.

Para ajudar os usuários a aproveitar ao máximo essa imagem de prévia, verifique os seguintes detalhes sobre o bloco:

  • Reflete o design mais recente. A prévia precisa representar com precisão o design mais atual do seu bloco.
  • Usa dimensões recomendadas. Para oferecer a melhor qualidade de exibição e uma boa experiência do usuário, a imagem de visualização precisa ter dimensões de 400 px por 400 px.
  • Usa um tema de cores estático. Use o tema de cor estática do bloco, não um dinâmico.
  • Inclui o ícone do app. Confirme se o ícone do app aparece na parte de cima da imagem de prévia.
  • Mostra o estado carregado/conectado. A prévia precisa mostrar um estado "carregado" ou "conectado" totalmente funcional, evitando conteúdo vazio ou de marcador de posição.
  • Use regras de resolução de recursos para personalização (opcional). Considere usar as regras de resolução de recursos do Android para fornecer prévias que correspondam ao tamanho da tela, idioma ou configurações de localidade do dispositivo. Isso é especialmente útil se a aparência do bloco variar em diferentes dispositivos.