Personalizar uma imagem

As imagens podem ser personalizadas usando propriedades em um elemento combinável Image (contentScale, colorFilter). Também é possível aplicar os Modifiers existentes para aplicar efeitos diferentes à Image. Os modificadores podem ser usados em qualquer elemento combinável, não apenas na Image combinável, enquanto contentScale e colorFilter são parâmetros explícitos nela.

Escala de conteúdo

Especifique uma opção contentScale para cortar ou mudar o dimensionamento de uma imagem dentro dos limites dela. Por padrão, se você não especificar uma opção contentScale, ContentScale.Fit vai ser usado.

No exemplo abaixo, a imagem combinável é restrita a 150 dp com uma borda, e o plano de fundo é definido como amarelo na Image combinável para mostrar as diferentes opções ContentScale na tabela abaixo.

val imageModifier = Modifier
    .size(150.dp)
    .border(BorderStroke(1.dp, Color.Black))
    .background(Color.Yellow)
Image(
    painter = painterResource(id = R.drawable.dog),
    contentDescription = stringResource(id = R.string.dog_content_description),
    contentScale = ContentScale.Fit,
    modifier = imageModifier
)

Definir opções ContentScale diferentes vai resultar em resultados diferentes. Confira abaixo uma tabela que pode ajudar a escolher o modo ContentScale correto necessário:

Imagem de origem Imagem de origem em modo retrato Cenário da imagem de origem
ContentScale Resultado – Imagem em modo retrato: Resultado – Imagem em modo paisagem:
ContentScale.Fit: redimensiona a imagem de maneira uniforme, mantendo a proporção (padrão). Se o conteúdo for menor que o tamanho, a imagem vai ser dimensionada na vertical para se ajustar aos limites. ContentScale.Fit em modo retrato ContentScale.Fit em modo paisagem
ContentScale.Crop: corta a imagem no meio para se ajustar ao espaço disponível. ContentScale.Crop em modo retrato ContentScale.Crop em modo paisagem
ContentScale.FillHeight: redimensiona a imagem de origem mantendo a proporção para que os limites correspondam à altura do destino. ContentScale.FillHeight em modo retrato ContentScale.FillHeight em modo paisagem
ContentScale.FillWidth: redimensiona a imagem de origem mantendo a proporção para que os limites correspondam à largura do destino. ContentScale.FillWidth em modo retrato ContentScale.FillWidth em modo paisagem
ContentScale.FillBounds: redimensiona o conteúdo na vertical e na horizontal de maneira não uniforme para preencher os limites de destino. Observação: isso vai distorcer as imagens se você as colocar em contêineres que não correspondam à proporção exata delas. ContentScale.FillBounds em modo retrato ContentScale.FillBounds em modo paisagem
ContentScale.Inside: redimensiona a imagem de origem para manter a proporção dentro dos limites do destino. Se ela for menor ou igual ao destino nas duas dimensões, vai se comportar de maneira semelhante a "Nenhuma". O conteúdo sempre vai ficar dentro dos limites. Se o conteúdo for menor que os limites, a imagem não vai ser redimensionada. Imagem de origem maior que os limites: ContentScale.Inside em modo retrato, imagem de origem maior que os limites Imagem de origem menor que os limites: ContentScale.Inside em modo retrato, imagem de origem menor que os limites Imagem de origem maior que os limites: ContentScale.Inside em modo paisagem, imagem de origem maior que os limites Imagem de origem menor que os limites: ContentScale.Inside em modo paisagem, imagem de origem menor que os limites
ContentScale.None: não redimensiona a imagem de origem. Se o conteúdo for menor que os limites de destino, ele não vai ser redimensionado para se ajustar à área. Imagem de origem maior que os limites: ContentScale.None em modo retrato, imagem de origem maior que os limites Imagem de origem menor que os limites: ContentScale.None em modo retrato, imagem de origem menor que os limites Imagem de origem maior que os limites: ContentScale.None em modo paisagem, imagem de origem maior que os limites Imagem de origem menor que os limites: ContentScale.None em modo paisagem, imagem de origem menor que os limites

Cortar um elemento combinável Image em uma forma

Para que uma imagem se encaixe em uma forma, use o modificador clip integrado. Para cortar uma imagem em formato de círculo, use Modifier.clip(CircleShape):

Image(
    painter = painterResource(id = R.drawable.dog),
    contentDescription = stringResource(id = R.string.dog_content_description),
    contentScale = ContentScale.Crop,
    modifier = Modifier
        .size(200.dp)
        .clip(CircleShape)
)

Cortar uma imagem com CircleShape
Figura 1: imagem cortada com CircleShape.

Forma de canto arredondado: use Modifier.clip(RoundedCornerShape(16.dp) com o tamanho dos cantos que você quer arredondar:

Image(
    painter = painterResource(id = R.drawable.dog),
    contentDescription = stringResource(id = R.string.dog_content_description),
    contentScale = ContentScale.Crop,
    modifier = Modifier
        .size(200.dp)
        .clip(RoundedCornerShape(16.dp))
)

Cortar uma imagem com RoundedCornerShape
Figura 2: imagem cortada com RoundedCornerShape.

Também é possível criar sua própria forma de recorte estendendo Shape e fornecendo um Path para que a forma seja cortada:

class SquashedOval : Shape {
    override fun createOutline(
        size: Size,
        layoutDirection: LayoutDirection,
        density: Density
    ): Outline {
        val path = Path().apply {
            // We create an Oval that starts at ¼ of the width, and ends at ¾ of the width of the container.
            addOval(
                Rect(
                    left = size.width / 4f,
                    top = 0f,
                    right = size.width * 3 / 4f,
                    bottom = size.height
                )
            )
        }
        return Outline.Generic(path = path)
    }
}

Image(
    painter = painterResource(id = R.drawable.dog),
    contentDescription = stringResource(id = R.string.dog_content_description),
    contentScale = ContentScale.Crop,
    modifier = Modifier
        .size(200.dp)
        .clip(SquashedOval())
)

Cortar uma imagem com forma de caminho personalizada
Figura 3: imagem cortada com uma forma de caminho personalizada.

Adicionar uma borda a um elemento combinável Image

Uma operação comum é combinar Modifier.border() com Modifier.clip() para criar uma borda ao redor de uma imagem:

val borderWidth = 4.dp
Image(
    painter = painterResource(id = R.drawable.dog),
    contentDescription = stringResource(id = R.string.dog_content_description),
    contentScale = ContentScale.Crop,
    modifier = Modifier
        .size(150.dp)
        .border(
            BorderStroke(borderWidth, Color.Yellow),
            CircleShape
        )
        .padding(borderWidth)
        .clip(CircleShape)
)

Cortar uma imagem e inserir uma borda ao redor
Figura 4: imagem cortada e com uma borda ao redor.

Se você quiser criar uma borda de gradiente, use a API Brush para desenhar uma borda de gradiente do arco-íris na imagem:

val rainbowColorsBrush = remember {
    Brush.sweepGradient(
        listOf(
            Color(0xFF9575CD),
            Color(0xFFBA68C8),
            Color(0xFFE57373),
            Color(0xFFFFB74D),
            Color(0xFFFFF176),
            Color(0xFFAED581),
            Color(0xFF4DD0E1),
            Color(0xFF9575CD)
        )
    )
}
val borderWidth = 4.dp
Image(
    painter = painterResource(id = R.drawable.dog),
    contentDescription = stringResource(id = R.string.dog_content_description),
    contentScale = ContentScale.Crop,
    modifier = Modifier
        .size(150.dp)
        .border(
            BorderStroke(borderWidth, rainbowColorsBrush),
            CircleShape
        )
        .padding(borderWidth)
        .clip(CircleShape)
)

Borda do círculo com gradiente de arco-íris
Figura 5: borda do círculo com gradiente de arco-íris.

Definir uma proporção personalizada

Para transformar uma imagem em uma proporção personalizada, use Modifier.aspectRatio(16f/9f) para fornecer uma proporção personalizada para uma imagem (ou qualquer elemento combinável para esse caso).

Image(
    painter = painterResource(id = R.drawable.dog),
    contentDescription = stringResource(id = R.string.dog_content_description),
    modifier = Modifier.aspectRatio(16f / 9f)
)

Usar Modifier.aspectRatio(16f/9f) na imagem
Figura 6: imagem usando Modifier.aspectRatio(16f/9f).

Filtro de cores: transformar as cores de pixel da imagem

A imagem combinável tem um parâmetro colorFilter que pode mudar a saída de pixels individuais da imagem.

Como tonalizar uma imagem

O uso de ColorFilter.tint(color, blendMode) vai aplicar um modo de combinação com a cor especificada na Image combinável. ColorFilter.tint(color, blendMode) usa BlendMode.SrcIn para colorir o conteúdo, o que significa que a cor fornecida vai ser mostrada na área em que a imagem for exibida na tela. Isso é útil para ícones e veículos que precisam ter um tema diferente.

Image(
    painter = painterResource(id = R.drawable.baseline_directions_bus_24),
    contentDescription = stringResource(id = R.string.bus_content_description),
    colorFilter = ColorFilter.tint(Color.Yellow)
)

ColorFilter.tint aplicado com BlendMode.SrcIn
Figura 7: ColorFilter.tint aplicado com BlendMode.SrcIn.

Outros BlendModes resultam em efeitos diferentes. Por exemplo, definir BlendMode.Darken com uma Color.Green em uma imagem produz o seguinte resultado:

Image(
    painter = painterResource(id = R.drawable.dog),
    contentDescription = stringResource(id = R.string.dog_content_description),
    colorFilter = ColorFilter.tint(Color.Green, blendMode = BlendMode.Darken)
)

Tonalidade Color.Green com BlendMode.Darken
Figura 8: tonalidade Color.Green com BlendMode.Darken.

Consulte a documentação de referência do BlendMode para ver mais informações sobre os diferentes modos de combinação disponíveis.

Como aplicar um filtro Image com matriz de cores

Transforme sua imagem usando a opção de matriz de cor ColorFilter. Por exemplo, para aplicar um filtro preto e branco nas imagens, use a ColorMatrix e defina a saturação como 0f.

Image(
    painter = painterResource(id = R.drawable.dog),
    contentDescription = stringResource(id = R.string.dog_content_description),
    colorFilter = ColorFilter.colorMatrix(ColorMatrix().apply { setToSaturation(0f) })
)

Matriz de cores com saturação 0 (imagem em preto e branco)
Figura 9: matriz de cores com saturação 0 (imagem em preto e branco).

Ajustar o contraste ou o brilho de um elemento combinável Image

Para mudar o contraste e o brilho de uma imagem, use a ColorMatrix para mudar os valores:

val contrast = 2f // 0f..10f (1 should be default)
val brightness = -180f // -255f..255f (0 should be default)
val colorMatrix = floatArrayOf(
    contrast, 0f, 0f, 0f, brightness,
    0f, contrast, 0f, 0f, brightness,
    0f, 0f, contrast, 0f, brightness,
    0f, 0f, 0f, 1f, 0f
)
Image(
    painter = painterResource(id = R.drawable.dog),
    contentDescription = stringResource(id = R.string.dog_content_description),
    colorFilter = ColorFilter.colorMatrix(ColorMatrix(colorMatrix))
)

Ajuste de brilho e contraste de imagem usando ColorMatrix
Figura 10: ajuste de brilho e contraste de imagem usando ColorMatrix.

Inverter cores de um elemento combinável Image

Para inverter as cores de uma imagem, defina a ColorMatrix para inverter as cores:

val colorMatrix = floatArrayOf(
    -1f, 0f, 0f, 0f, 255f,
    0f, -1f, 0f, 0f, 255f,
    0f, 0f, -1f, 0f, 255f,
    0f, 0f, 0f, 1f, 0f
)
Image(
    painter = painterResource(id = R.drawable.dog),
    contentDescription = stringResource(id = R.string.dog_content_description),
    colorFilter = ColorFilter.colorMatrix(ColorMatrix(colorMatrix))
)

Cores invertidas na imagem
Figura 11: cores invertidas na imagem.

Desfocar um elemento combinável Image

Para desfocar uma imagem, use Modifier.blur(), fornecendo radiusX e radiusY, que especificam o raio de desfoque na direção horizontal e vertical, respectivamente.

Image(
    painter = painterResource(id = R.drawable.dog),
    contentDescription = stringResource(id = R.string.dog_content_description),
    contentScale = ContentScale.Crop,
    modifier = Modifier
        .size(150.dp)
        .blur(
            radiusX = 10.dp,
            radiusY = 10.dp,
            edgeTreatment = BlurredEdgeTreatment(RoundedCornerShape(8.dp))
        )
)

BlurEffect aplicado à imagem
Figura 12: BlurEffect aplicado à imagem.

Ao desfocar Images, é recomendável usar BlurredEdgeTreatment(Shape), em vez de BlurredEdgeTreatment.Unbounded, já que o último é usado para desfocar renderizações arbitrárias que precisam ser renderizadas fora dos limites do conteúdo original. Para imagens, é provável que elas não sejam renderizadas fora dos limites do conteúdo. O desfoque de um retângulo arredondado pode exigir essa distinção.

Por exemplo, se definirmos BlurredEdgeTreatment como "Unbounded" na imagem acima, as bordas da imagem vão aparecer desfocadas em vez de nítidas:

Image(
    painter = painterResource(id = R.drawable.dog),
    contentDescription = stringResource(id = R.string.dog_content_description),
    contentScale = ContentScale.Crop,
    modifier = Modifier
        .size(150.dp)
        .blur(
            radiusX = 10.dp,
            radiusY = 10.dp,
            edgeTreatment = BlurredEdgeTreatment.Unbounded
        )
        .clip(RoundedCornerShape(8.dp))
)

BlurEdgeTreatment.Unbounded
Figura 13: BlurEdgeTreatment.Unbounded.