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 các đối tượng sửa đổi 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ụ sau, 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 để minh hoạ các tuỳ chọn ContentScale khác nhau trong bảng bên dưới.

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. Bảng sau đây giúp bạn chọn đúng chế độ ContentScale:

Hình ảnh nguồn Nguồn chân dung cho thấy một chú chó. Nguồn phong cảnh, trong đó có một chú chó khác.
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. Ảnh chân dung một chú chó được điều chỉnh theo tỷ lệ đồng nhất. Một bức ảnh phong cảnh về chú chó được điều chỉnh theo tỷ lệ đồng nhất.
ContentScale.Crop: Cắt giữa hình ảnh vào khoảng trống có sẵn. Hình ảnh chân dung bị cắt để lấp đầy khung hình vuông, chỉ hiển thị phần trung tâm của hình ảnh. Hình ảnh ngang bị cắt để lấp đầy khung hình vuông, chỉ hiển thị phần trung tâm của hình ảnh.
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. Một hình ảnh dọc được điều chỉnh tỷ lệ để lấp đầy chiều cao của khung hình vuông, cho thấy toàn bộ hình ảnh với nền màu vàng xuất hiện ở bên trái và bên phải. Một hình ảnh ngang được điều chỉnh tỷ lệ để lấp đầy chiều cao của một khung hình vuông, với các cạnh bị cắt.
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 được điều chỉnh tỷ lệ để lấp đầy chiều rộng của khung hình vuông, với phần trên và dưới bị cắt. Một hình ảnh ngang được điều chỉnh tỷ lệ để lấp đầy chiều rộng của khung hình vuông, cho thấy hình ảnh đầy đủ với nền màu vàng xuất hiện ở trên cùng và dưới cùng.
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 chân dung bị biến dạng để lấp đầy hoàn toàn một khung hình vuông, kéo giãn hình ảnh. Hình ảnh ngang bị biến dạng để lấp đầy hoàn toàn một khung hình vuông, kéo giãn hình ảnh.
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ố này sẽ hoạt động tương tự như None. Nội dung sẽ luôn nằm trong phạm vi. 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: Một hình ảnh dọc, ban đầu lớn hơn giới hạn vuông của nó, được thu nhỏ để vừa với giới hạn trong khi vẫn duy trì tỷ lệ khung hình, cho thấy nền màu vàng ở hai bên. Hình ảnh nguồn nhỏ hơn giới hạn: Một hình ảnh dọc, ban đầu nhỏ hơn giới hạn vuông, được hiển thị ở kích thước ban đầu trong khung, cho thấy nền màu vàng xung quanh. Hình ảnh nguồn lớn hơn giới hạn: Một hình ảnh ngang, ban đầu có kích thước lớn hơn giới hạn vuông, được thu nhỏ để vừa với khung hình trong khi vẫn duy trì tỷ lệ khung hình, cho thấy nền màu vàng ở trên cùng và dưới cùng. Hình ảnh nguồn nhỏ hơn giới hạn: Một hình ảnh ngang, ban đầu nhỏ hơn giới hạn vuông, được hiển thị ở kích thước ban đầu trong khung, cho thấy nền màu vàng xung quanh.
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: Một hình ảnh dọc, ban đầu có kích thước lớn hơn giới hạn vuông, được hiển thị ở kích thước ban đầu, với các phần mở rộng ra ngoài phần trên và dưới của khung hình. Hình ảnh nguồn nhỏ hơn giới hạn: Một hình ảnh dọc, ban đầu nhỏ hơn giới hạn vuông, được hiển thị ở kích thước ban đầu trong khung, cho thấy nền màu vàng xung quanh. Hình ảnh nguồn lớn hơn giới hạn: Một hình ảnh ngang, ban đầu có kích thước lớn hơn giới hạn vuông, được hiển thị ở kích thước ban đầu, với các phần mở rộng ra ngoài bên trái và bên phải khung hình. Hình ảnh nguồn nhỏ hơn giới hạn: Một hình ảnh ngang, ban đầu nhỏ hơn giới hạn vuông, được hiển thị ở kích thước ban đầu trong khung, cho thấy nền màu vàng xung quanh.

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

Hình ảnh một chú chó được cắt thành hình tròn hoàn hảo.
Hình 1. Cắt hình ảnh bằng CircleShape.

Đối với hình dạng góc bo tròn, hãy 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))
)

Hình ảnh vuông về một chú chó được cắt bằng các góc bo tròn.
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())
)

Hình ảnh vuông của một chú chó được cắt thành hình bầu dục 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)
)

Hình ảnh vuông của một chú chó, được cắt thành hình tròn, có đường viền màu vàng xung quanh hình tròn.
Hình 4. Hình ảnh bị cắt có đường viền xung quanh.

Để tạo đường viền chuyển màu, 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)
)

Hình ảnh tròn của một chú chó có đường viền chuyển màu cầu vồng xung quanh hình tròn.
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)
)

Hình ảnh vuông của một chú chó, được chuyển đổi thành tỷ lệ khung hình 16:9, giúp hình ảnh rộng hơn và ngắn hơn.
Hình 6. Sử dụng Modifier.aspectRatio(16f/9f) trên Image.

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

Hình ảnh một chiếc xe buýt có áp dụng tông màu vàng.
Hình 7. ColorFilter.tint đã được áp dụng với BlendMode.SrcIn.

Các BlendMode khác sẽ tạo ra 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)
)

Một chú chó được áp dụng sắc thái màu xanh lục bằng BlendMode.Darken, tạo ra các sắc thái màu xanh lục đậm hơn.
Hình 8. Color.Green tint bằng BlendMode.Darken.

Hãy 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 bằng 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) })
)

Một chú chó được áp dụng bộ lọc đen trắng, loại bỏ tất cả độ bão hoà màu.
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))
)

Một chú chó có độ sáng và độ tương phản cao hơn, khiến chú chó trông sống động hơn.
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ột chú chó có màu sắc đảo ngược, tạo hiệu ứng giống như âm bản.
Hình 11. Màu đảo ngược trên hình ảnh.

Làm mờ 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))
        )
)

Một chú chó được áp dụng hiệu ứng làm mờ mạnh, khiến chú chó trông không rõ ràng và bị mất nét.
Hình 12. BlurEffect được áp dụng cho một 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ước đó, 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))
)

Hình ảnh bị mờ của một chú chó, trong đó hiệu ứng mờ mở rộng ra ngoài ranh giới của hình ảnh gốc, khiến các cạnh bị nhoè.
Hình 13. BlurEdgeTreatment.Unbounded.