Restrições e ordem dos modificadores

No Compose, é possível encadear vários modificadores para mudar a aparência de um elemento combinável. Essas cadeias de modificadores podem afetar as restrições transmitidas para elementos combináveis, que definem os limites de largura e altura.

Esta página descreve como os modificadores encadeados afetam as restrições e, por sua vez, a medição e a colocação de elementos combináveis.

Modificadores na árvore da interface

Para entender como os modificadores influenciam uns aos outros, é útil visualizar como eles aparecem na árvore da interface, que é gerada durante a fase de composição. Para mais informações, consulte a seção Composição.

Na árvore da interface, é possível conferir os modificadores como nós de wrapper para os nós de layout:

Código para elementos combináveis e modificadores, além da representação visual deles como uma árvore da interface.
Figura 1. Modificadores que agrupam nós de layout na árvore da interface.

Adicionar mais de um modificador a um elemento combinável cria uma cadeia de modificadores. Quando você encadeia vários modificadores, cada nó de modificador encapsula o restante da cadeia e o nó de layout dentro. Por exemplo, quando você encadeia um clip e um modificador size, o nó de modificador clip envolve o nó de modificador size, que envolve o nó de layout Image.

Na fase de layout, o algoritmo que percorre a árvore permanece o mesmo, mas cada nó de modificador também é visitado. Dessa forma, um modificador pode mudar os requisitos de tamanho e o posicionamento do modificador ou do nó de layout encapsulado.

Conforme mostrado na Figura 2, a implementação dos elementos combináveis Image e Text consiste em uma cadeia de modificadores que envolvem um único nó de layout. As implementações de Row e Column são simplesmente nós de layout que descrevem como exibir os filhos.

A estrutura de árvore anterior, mas agora cada nó é apenas um layout simples, com muitos modificadores que envolvem os nós.
Figura 2. A mesma estrutura de árvore da Figura 1, mas com elementos combináveis na árvore da interface visualizados como cadeias de modificadores.

Para resumir:

  • Os modificadores agrupam um único modificador ou nó de layout.
  • Os nós de layout podem organizar vários nós filhos.

As seções a seguir descrevem como usar esse modelo mental para refletir sobre o encadeamento de modificadores e como ele influencia o tamanho dos elementos combináveis.

Restrições na fase de layout

A fase de layout segue um algoritmo de três etapas para encontrar a largura, a altura e as coordenadas x e y de cada nó de layout:

  1. Medir filhos: um nó mede os filhos, se houver.
  2. Decidir o próprio tamanho: com base nessas medidas, um nó decide o próprio tamanho.
  3. Colocar filhos: cada nó filho é colocado em relação à própria posição de um nó.

O Constraints ajuda a encontrar os tamanhos certos para os nós durante as duas primeiras etapas do algoritmo. As restrições definem os limites mínimo e máximo para a largura e a altura de um nó. Quando o nó decide o tamanho, o tamanho medido precisa estar dentro dessa faixa.

Tipos de restrições

Uma restrição pode ser uma das seguintes:

  • Limitado: o nó tem largura e altura máximas e mínimas.
Restrições limitadas de tamanhos diferentes dentro de um contêiner.
Figura 3. Limitações limitadas.
  • Ilimitado: o nó não é restrito a nenhum tamanho. Os limites máximos de largura e altura são definidos como infinito.
Restrições ilimitadas com largura e altura definidas como infinito. As restrições se estendem além do contêiner.
Figura 4. Restrições ilimitadas.
  • Exata: o nó precisa seguir um requisito de tamanho exato. Os limites mínimo e máximo são definidos com o mesmo valor.
Restrições exatas que se conformam a um requisito de tamanho exato no contêiner.
Figura 5. Restrições exatas.
  • Combinação: o nó segue uma combinação dos tipos de restrição acima. Por exemplo, uma restrição pode limitar a largura, permitindo uma altura máxima ilimitada, ou definir uma largura exata, mas fornecer uma altura limitada.
Dois contêineres que mostram combinações de restrições limitadas e ilimitadas e larguras e alturas exatas.
Figura 6. Combinações de restrições limitadas e ilimitadas e larguras e alturas exatas.

A próxima seção descreve como essas restrições são transmitidas de um pai para um filho.

Como as restrições são transmitidas do pai para o filho

Durante a primeira etapa do algoritmo descrito em Restrições na fase de layout, as restrições são transmitidas de pai para filho na árvore da interface.

Quando um nó pai mede os filhos, ele fornece essas restrições a cada filho para que eles saibam o tamanho que podem ter. Em seguida, quando ele decidir o próprio tamanho, ele também vai aderir às restrições transmitidas pelos próprios pais.

Em um nível alto, o algoritmo funciona da seguinte maneira:

  1. Para decidir o tamanho que realmente quer ocupar, o nó raiz na árvore da interface mede os filhos e encaminha as mesmas restrições para o primeiro filho.
  2. Se o filho for um modificador que não afeta a medição, ele vai encaminhar as restrições para o próximo modificador. As restrições são transmitidas para a cadeia de modificadores como estão, a menos que um modificador que afete a medição seja alcançado. O tamanho das restrições é alterado de acordo com isso.
  3. Quando um nó é alcançado que não tem filhos (chamado de "nó de folha"), ele decide o tamanho com base nas restrições transmitidas e retorna esse tamanho resolvido para o pai.
  4. O pai adapta as restrições com base nas medições do filho e chama o próximo filho com essas restrições ajustadas.
  5. Depois que todos os filhos de um pai são medidos, o nó pai decide o próprio tamanho e os comunica ao próprio pai.
  6. Dessa forma, toda a árvore é percorrida em profundidade. Eventualmente, todos os nós decidem os tamanhos e a etapa de medição é concluída.

Para conferir um exemplo detalhado, assista ao vídeo Ordem de restrições e modificadores.

Modificadores que afetam restrições

Você aprendeu na seção anterior que alguns modificadores podem afetar o tamanho da restrição. As seções a seguir descrevem modificadores específicos que afetam as restrições.

Modificador size

O modificador size declara o tamanho preferencial do conteúdo.

Por exemplo, a árvore de interface a seguir precisa ser renderizada em um contêiner de 300dp por 200dp. As restrições são limitadas, permitindo larguras entre 100dp e 300dp e alturas entre 100dp e 200dp:

Uma parte de uma árvore da interface com o modificador de tamanho envolvendo um nó de layout e a
  representação das restrições limitadas definidas pelo modificador de tamanho em um contêiner.
Figura 7. Restrições limitadas na árvore da interface e a representação delas em um contêiner.

O modificador size adapta as restrições recebidas para corresponder ao valor transmitido. Neste exemplo, o valor é 150dp:

É igual à Figura 7, exceto pelo modificador de tamanho que adapta as restrições recebidas para corresponder ao valor transmitido.
Figura 8. O modificador size ajusta as restrições para 150dp.

Se a largura e a altura forem menores que o menor limite de restrição ou maiores que o maior limite de restrição, o modificador vai corresponder às restrições transmitidas o mais próximo possível, mantendo as restrições transmitidas:

Duas árvores de interface e as representações correspondentes em contêineres. No primeiro, o
  modificador de tamanho aceita as restrições de entrada. No segundo, o modificador de tamanho se adapta às
  restrições muito grandes o máximo possível, resultando em restrições que preenchem o contêiner.
Figura 9. O modificador size aderiu à restrição transmitida o máximo possível.

O encadeamento de vários modificadores size não funciona. O primeiro modificador size define as restrições mínima e máxima como um valor fixo. Mesmo que o segundo modificador de tamanho solicite um tamanho menor ou maior, ele ainda precisa seguir os limites exatos transmitidos para não substituir esses valores:

Uma cadeia de dois modificadores de tamanho na árvore da interface e a representação dela em um contêiner,
  que é o resultado do primeiro valor transmitido, e não do segundo.
Figura 10. Uma cadeia de dois modificadores size, em que o segundo valor transmitido (50dp) não substitui o primeiro valor (100dp).

Modificador requiredSize

Use o modificador requiredSize em vez de size se precisar que o nó substitua as restrições recebidas. O modificador requiredSize substitui as restrições recebidas e transmite o tamanho especificado como limites exatos.

Quando o tamanho é transmitido de volta para a árvore, o nó filho é centralizado no espaço disponível:

O modificador de tamanho e requiredSize encadeado em uma árvore de interface e a representação
  correspondente em um contêiner. As restrições do modificador requiredSize substituem as restrições do modificador de
  tamanho.
Figura 11. O modificador requiredSize substitui as restrições de entrada do modificador size.

Modificadores width e height

O modificador size adapta a largura e a altura das restrições. Com o modificador width, é possível definir uma largura fixa, mas deixar a altura indefinida. Da mesma forma, com o modificador height, é possível definir uma altura fixa, mas deixar a largura indefinida:

Duas árvores de interface, uma com o modificador de largura e a representação do contêiner e a outra
  com o modificador de altura e a representação dele.
Figura 12. O modificador width e o modificador height definem uma largura e altura fixas, respectivamente.

Modificador sizeIn

O modificador sizeIn permite definir restrições mínimas e máximas exatas para largura e altura. Use o modificador sizeIn se precisar de um controle refinado sobre as restrições.

Uma árvore de interface com o modificador sizeIn com larguras e alturas mínimas e máximas definidas
  e a representação dela em um contêiner.
Figura 13. O modificador sizeIn com minWidth, maxWidth, minHeight e maxHeight definido.

Exemplos

Esta seção mostra e explica a saída de vários snippets de código com modificadores encadeados.

Image(
    painterResource(R.drawable.hero),
    contentDescription = null,
    Modifier
        .fillMaxSize()
        .size(50.dp)
)

Esse snippet produz a seguinte saída:

  • O modificador fillMaxSize muda as restrições para definir a largura e a altura mínimas para o valor máximo: 300dp de largura e 200dp de altura.
  • Mesmo que o modificador size queira usar um tamanho de 50dp, ele ainda precisa seguir as restrições mínimas de entrada. Portanto, o modificador size também vai gerar os limites de restrição exatos de 300 por 200, ignorando efetivamente o valor fornecido no modificador size.
  • O Image segue esses limites e informa um tamanho de 300 por 200, que é transmitido em toda a árvore.

Image(
    painterResource(R.drawable.hero),
    contentDescription = null,
    Modifier
        .fillMaxSize()
        .wrapContentSize()
        .size(50.dp)
)

Esse snippet produz a seguinte saída:

  • O modificador fillMaxSize adapta as restrições para definir a largura mínima e a altura para o valor máximo: 300dp na largura e 200dp na altura.
  • O modificador wrapContentSize redefine as restrições mínimas. Assim, enquanto fillMaxSize resultou em restrições fixas, wrapContentSize redefiniu-o para restrições limitadas. O nó a seguir agora pode ocupar todo o espaço novamente ou ser menor que ele.
  • O modificador size define as restrições para os limites mínimo e máximo de 50.
  • O Image é resolvido para um tamanho de 50 por 50, e o modificador size encaminha isso.
  • O modificador wrapContentSize tem uma propriedade especial. Ele pega o elemento filho e o coloca no centro dos limites mínimos disponíveis que foram transmitidos a ele. O tamanho comunicado aos pais é igual aos limites mínimos transmitidos.

Combinando apenas três modificadores, é possível definir um tamanho para o elemento combinável e centralizá-lo no elemento pai.

Image(
    painterResource(R.drawable.hero),
    contentDescription = null,
    Modifier
        .clip(CircleShape)
        .padding(10.dp)
        .size(100.dp)
)

Esse snippet produz a seguinte saída:

  • O modificador clip não muda as restrições.
    • O modificador padding diminui as restrições máximas.
    • O modificador size define todas as restrições como 100dp.
    • O Image obedece a essas restrições e informa um tamanho de 100 por 100dp.
    • O modificador padding adiciona 10dp em todos os tamanhos, aumentando a largura e a altura informadas em 20dp.
    • Agora, na fase de exibição, o modificador clip atua em uma tela de 120 por 120dp. Assim, ele cria uma máscara circular desse tamanho.
    • Em seguida, o modificador padding insere o conteúdo com 10dp em todos os tamanhos, para reduzir o tamanho da tela para 100 por 100dp.
    • O Image é desenhado nessa tela. A imagem é recortada com base no círculo original de 120dp, de modo que a saída é um resultado não arredondado.