Tuỳ chỉnh hình ảnh

Bạn có thể tuỳ chỉnh hình ảnh bằng cách sử dụng các thuộc tính trên một thành phần kết hợp Image (contentScale, colorFilter). Bạn cũng có thể áp dụng Modifiers hiện có để dùng nhiều hiệu ứng cho Image. Bạn có thể sử dụng đối tượng sửa đổi trên mọi Thành phần kết hợp, không chỉ thành phần kết hợp Image, mặc dù contentScalecolorFilter là các tham số phải khai báo rõ ràng trên thành phần kết hợp Image.

Phạm vi của nội dung

Hãy chỉ định một tuỳ chọn contentScale để cắt hoặc điều chỉnh kích thước hình ảnh theo tỷ lệ bên trong giới hạn của hình ảnh đó. Theo mặc định, nếu bạn không chỉ định tuỳ chọn contentScale, thì ContentScale.Fit sẽ được sử dụng.

Trong ví dụ bên dưới, thành phần kết hợp Image bị hạn chế ở kích thước 150 dp với đường viền và nền được đặt thành màu vàng trên thành phần kết hợp Image để hiển thị các tuỳ chọn ContentScale khác nhau trong bảng dưới đây.

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
)

Việc đặt các tuỳ chọn ContentScale khác nhau sẽ dẫn đến kết quả khác nhau. Dưới đây là bảng có thể giúp bạn chọn đúng chế độ ContentScale mà bạn yêu cầu:

Hình ảnh nguồn Hình ảnh nguồn dạng dọc Hình ảnh nguồn dạng ngang
ContentScale Kết quả – Hình ảnh dọc: Kết quả – Hình ảnh ngang:
ContentScale.Fit: Điều chỉnh tỷ lệ hình ảnh một cách đồng nhất, giữ nguyên tỷ lệ khung hình (mặc định). Nếu nội dung nhỏ hơn kích thước, hình ảnh sẽ được tăng kích thước theo tỷ lệ cho vừa với giới hạn. Hình ảnh dọc dùng ContentScale.Fit Hình ảnh ngang dùng ContentScale.Fit
ContentScale.Crop: Cắt giữa hình ảnh vào khoảng trống có sẵn. Hình ảnh dọc dùng ContentScale.Crop Hình ảnh ngang dùng ContentScale.Crop
ContentScale.FillHeight: Điều chỉnh theo tỷ lệ nguồn để duy trì tỷ lệ khung hình sao cho các giới hạn khớp với chiều cao đích đến. Hình ảnh dọc dùng ContentScale.FillHeight Hình ảnh ngang dùng ContentScale.FillHeight
ContentScale.FillWidth: Điều chỉnh theo tỷ lệ nguồn để duy trì tỷ lệ khung hình sao cho các giới hạn khớp với chiều rộng đích đến. Hình ảnh dọc dùng ContentScale.FillWidth Hình ảnh ngang dùng ContentScale.FillWidth
ContentScale.FillBounds: Điều chỉnh nội dung theo tỷ lệ một cách không đồng nhất theo chiều dọc và chiều ngang để lấp đầy các giới hạn đích đến. (Lưu ý: Thao tác này sẽ làm méo hình ảnh nếu bạn đặt hình ảnh vào vùng chứa không phù hợp với tỷ lệ chính xác của hình ảnh) Hình ảnh dọc dùng ContentScale.FillBounds Hình ảnh ngang dùng ContentScale.FillBounds
ContentScale.Inside: Điều chỉnh nguồn theo tỷ lệ để duy trì tỷ lệ khung hình bên trong giới hạn đích đến. Nếu nguồn nhỏ hơn hoặc bằng đích đến ở cả hai kích thước, thì tham số sẽ hoạt động tương tự như "None". Nội dung sẽ luôn nằm trong giới hạn. Nếu nội dung nhỏ hơn giới hạn, thì không áp dụng điều chỉnh theo tỷ lệ. Hình ảnh nguồn lớn hơn giới hạn: Hình ảnh dọc dùng ContentScale.Inside, hình ảnh nguồn lớn hơn giới hạn Hình ảnh nguồn nhỏ hơn giới hạn: Hình ảnh dọc dùng ContentScale.Inside, hình ảnh nguồn nhỏ hơn giới hạn Hình ảnh nguồn lớn hơn giới hạn: Hình ảnh ngang dùng ContentScale.Inside, hình ảnh nguồn lớn hơn giới hạn Hình ảnh nguồn nhỏ hơn giới hạn: Hình ảnh ngang dùng ContentScale.Inside, hình ảnh nguồn nhỏ hơn giới hạn
ContentScale.None: Không điều chỉnh theo tỷ lệ cho nguồn. Nếu nội dung nhỏ hơn giới hạn đích đến, nội dung đó sẽ không được tăng kích thước theo tỷ lệ để phù hợp với khu vực. Hình ảnh nguồn lớn hơn giới hạn: Hình ảnh dọc dùng ContentScale.None, hình ảnh nguồn lớn hơn giới hạn Hình ảnh nguồn nhỏ hơn giới hạn: Hình ảnh dọc dùng ContentScale.None, hình ảnh nguồn nhỏ hơn giới hạn Hình ảnh nguồn lớn hơn giới hạn: Hình ảnh ngang dùng ContentScale.None, hình ảnh nguồn lớn hơn giới hạn Hình ảnh nguồn nhỏ hơn giới hạn: Hình ảnh ngang dùng ContentScale.None, hình ảnh nguồn nhỏ hơn giới hạn

Cắt thành phần kết hợp Image thành một hình dạng

Để giúp hình ảnh vừa với một hình dạng, hãy sử dụng đối tượng sửa đổi clip tích hợp sẵn. Để cắt một hình ảnh thành hình tròn, hãy dùng 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)
)

Cắt hình ảnh bằng CircleShape
Hình 1: Cắt hình ảnh bằng CircleShape

Hình bo tròn – dùng Modifier.clip(RoundedCornerShape(16.dp)) với kích thước của các góc mà bạn muốn bo tròn:

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))
)

Cắt hình ảnh bằng RoundedCornerShape
Hình 2: Cắt hình ảnh bằng RoundedCornerShape

Bạn cũng có thể tạo hình dạng cắt của riêng mình bằng cách mở rộng Shape và cung cấp một Path cho hình dạng cần cắt xung quanh:

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())
)

Cắt hình ảnh bằng hình dạng đường dẫn tuỳ chỉnh
Hình 3: Cắt hình ảnh bằng hình dạng đường dẫn tuỳ chỉnh

Thêm đường viền vào thành phần kết hợp Image

Một thao tác thường dùng là kết hợp Modifier.border() với Modifier.clip() để tạo đường viền xung quanh hình ảnh:

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)
)

Cắt một hình ảnh và cung cấp đường viền xung quanh hình ảnh đó
Hình 4: Cắt một hình ảnh và cung cấp đường viền xung quanh hình ảnh đó

Nếu muốn tạo đường viền chuyển màu (gradient), bạn có thể sử dụng API Brush để vẽ đường viền chuyển màu có màu cầu vồng xung quanh hình ảnh:

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)
)

Đường viền vòng tròn chuyển màu có màu cầu vồng
Hình 5: Đường viền vòng tròn chuyển màu có màu cầu vồng

Đặt tỷ lệ khung hình tuỳ chỉnh

Để chuyển đổi một hình ảnh thành tỷ lệ khung hình tuỳ chỉnh, hãy sử dụng Modifier.aspectRatio(16f/9f) để cung cấp tỷ lệ tuỳ chỉnh cho hình ảnh (hoặc bất kỳ thành phần kết hợp nào).

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

Sử dụng Modifier.aspectRatio(16f/9f) trên Hình ảnh
Hình 6: Sử dụng Modifier.aspectRatio(16f/9f) trên Hình ảnh

Bộ lọc màu – Chuyển đổi màu pixel của hình ảnh

Thành phần kết hợp Image có một tham số colorFilter có thể thay đổi đầu ra của từng pixel riêng lẻ cho hình ảnh của bạn.

Phủ màu hình ảnh

Việc sử dụng ColorFilter.tint(color, blendMode) sẽ áp dụng chế độ kết hợp với màu đã cho vào thành phần kết hợp Image. ColorFilter.tint(color, blendMode) sử dụng BlendMode.SrcIn để phủ màu nội dung, nghĩa là màu được cung cấp sẽ hiển thị, tại nơi hình ảnh được hiển thị trên màn hình. Điều này rất hữu ích đối với các biểu tượng và vectơ cần phải có chủ đề khác nhau.

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 được áp dụng với BlendMode.SrcIn
Hình 7: ColorFilter.tint được áp dụng với BlendMode.SrcIn

Các kết quả khác của BlendMode trong các hiệu ứng khác nhau. Chẳng hạn, việc đặt BlendMode.Darken cùng với Color.Green trên hình ảnh sẽ tạo ra kết quả sau:

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

Color.Green phủ màu với BlendMode.Darken
Hình 8: Color.Green phủ màu với BlendMode.Darken

Xem tài liệu tham khảo về BlendMode để biết thêm thông tin về các chế độ kết hợp khác hiện có.

Áp dụng bộ lọc Image với ma trận màu

Biến đổi hình ảnh bằng cách sử dụng tuỳ chọn ma trận màu ColorFilter. Ví dụ: để áp dụng bộ lọc đen trắng trên hình ảnh, bạn có thể sử dụng ColorMatrix và đặt độ bão hoà thành 0f.

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

Ma trận màu có độ bão hoà là 0 (hình ảnh đen trắng)
Hình 9: Ma trận màu có độ bão hoà là 0 (hình ảnh đen trắng)

Điều chỉnh độ tương phản hoặc độ sáng của thành phần kết hợp Image

Để thay đổi độ tương phản và độ sáng của hình ảnh, bạn có thể sử dụng ColorMatrix để thay đổi các giá trị:

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))
)

Điều chỉnh độ sáng và độ tương phản của hình ảnh bằng ColorMatrix
Hình 10: Điều chỉnh độ sáng và độ tương phản của hình ảnh bằng ColorMatrix

Đảo ngược màu của thành phần kết hợp Image

Để đảo ngược màu của hình ảnh, hãy đặt ColorMatrix để đảo ngược màu:

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))
)

Màu đảo ngược trên hình ảnh
Hình 11: Màu đảo ngược trên hình ảnh

Làm mờ một thành phần kết hợp Image

Để làm mờ hình ảnh, hãy sử dụng Modifier.blur(), cung cấp radiusXradiusY, trong đó chỉ định bán kính làm mờ theo chiều ngang và chiều dọc tương ứng.

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))
        )
)

Hiệu ứng BlurEffect áp dụng cho hình ảnh
Hình 12: Hiệu ứng BlurEffect áp dụng cho hình ảnh

Khi làm mờ Images, bạn nên sử dụng BlurredEdgeTreatment(Shape) thay vì BlurredEdgeTreatment.Unbounded, vì mã thứ hai dùng để làm mờ kết xuất hình ảnh tuỳ ý theo dự kiến sẽ hiển thị bên ngoài giới hạn của nội dung gốc. Đối với hình ảnh, có khả năng hình ảnh sẽ không hiển thị ngoài giới hạn nội dung; trong khi việc làm mờ hình chữ nhật góc tròn có thể cần sự khác biệt này.

Ví dụ: nếu chúng ta đặt BlurredEdgeTreatment thành Unbounded trên hình ảnh ở trên, các cạnh của hình ảnh sẽ xuất hiện mờ thay vì sắc nét:

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
Hình 13: BlurEdgeTreatment.Unbounded