Jetpack Compose 图片加载

744 阅读5分钟

从磁盘加载图片

您可以使用 [Image] 可组合项在屏幕上显示图形。如需从磁盘加载图片(例如 PNG、JPEG、WEBP)或矢量资源,请将 [painterResource] API 与图片引用搭配使用。您不必知道资源的类型,只需在 Image 或 paint 修饰符中使用 painterResource 即可。

image.png

Image(
    painter = painterResource(id = R.drawable.car),
    contentDescription = ""
)

从互联网加载图片

  • App 级的 build.gradle.kts 加入引用依赖
implementation("io.coil-kt:coil-compose:2.6.0")
  • AndroidManifest.xml 加入网络请求权限,以及非 https 可请求
<?xml version="1.0" encoding="utf-8"?>
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:tools="http://schemas.android.com/tools">

    <uses-permission android:name="android.permission.INTERNET"/>
    <application       
        android:networkSecurityConfig="@xml/network_security_config">
        ...
    </application>

</manifest>
  • res/xml/network_security_config.xml
<?xml version="1.0" encoding="utf-8"?>
<network-security-config>
    <base-config cleartextTrafficPermitted="true" />
</network-security-config>
  • 网络加载使用方法
AsyncImage(
    model = "https://img0.baidu.com/it/u=677830615,3557734461&fm=253&fmt=auto&app=120&f=JPEG?w=668&h=500",
    contentDescription = "",
    modifier = Modifier.fillMaxWidth(0.5f).aspectRatio(668f/500f)
)

内容缩放

指定 contentScale 选项以剪裁图片或更改图片在其边界内的缩放方式。默认情况下,如果您未指定 contentScale 选项,系统将使用 ContentScale.Fit

在以下示例中,Image 可组合项的尺寸限制为 150dp,并带有边框,且 Image 可组合项的背景设置为黄色,以展示下表中的不同 ContentScale 选项。

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
)

设置不同的 ContentScale 选项会生成不同的输出。下表可帮助您选择所需的正确 ContentScale 模式:

ContentScale纵向图片横向图片
图片
ContentScale.Fit
ContentScale.Crop
ContentScale.FillHeight
ContentScale.FillWidth
ContentScale.FillBounds
ContentScale.Inside来源图片大于边界

源图片小于边界
来源图片大于边界

源图片小于边界

将 Image 可组合项裁剪为形状

如需使图片适应形状,请使用内置的 clip 修饰符。如需将图片剪裁成圆形,请使用 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)
)

圆角形状 - 使用 Modifier.clip(RoundedCornerShape(16.dp) 并指定圆角形状的边角大小:

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

您也可以创建自己的剪裁形状,方法是扩展 Shape 并提供 Path 以指定形状剪裁路径:

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

为 Image 可组合项添加边框

一种常见的操作是结合使用 Modifier.border() 与 Modifier.clip() 在图片周围创建边框:

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

如果您想要创建渐变边框,可以使用 Brush API 在图片周围绘制彩虹渐变边框:

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

设置自定义宽高比

如需将图片转换为自定义宽高比,请使用 Modifier.aspectRatio(16f/9f) 为图片(或任何相关的可组合项)提供自定义宽高比。

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

颜色滤镜 - 转换图片的像素颜色

Image 可组合项包含一个 colorFilter 参数,可用于更改图片中各个像素的输出。

对图片进行色调调节

使用 ColorFilter.tint(color, blendMode) 会将具有指定颜色的混合模式应用于 Image 可组合项。ColorFilter.tint(color, blendMode) 使用 BlendMode.SrcIn 对内容进行色调调节,这意味着系统将在屏幕上显示图片的位置显示所提供的颜色。这对于需要以不同方式设置主题的图标和矢量非常有用。

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

其他 BlendMode 的结果有所不同。例如,对图片设置 BlendMode.Darken 和 Color.Green 会产生以下效果:

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

如需详细了解可用的不同混合模式,请参阅 BlendMode 参考文档

通过颜色矩阵应用 Image 滤镜

颜色矩阵 ColorFilter 选项可用于转换图片。例如,如需对图片应用黑白滤镜,您可以使用 ColorMatrix 并将饱和度设置为 0f

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

调整 Image 可组合项的对比度或亮度

如需更改图片的对比度和亮度,您可以使用 ColorMatrix 更改多项值:

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

反转 Image 可组合项的颜色

如需反转图片的颜色,请设置 ColorMatrix 以反转颜色:

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

对 Image 可组合项进行模糊处理

如需对图片进行模糊处理,请使用 Modifier.blur() 并同时提供 radiusX 和 radiusY,两者分别指定水平方向和垂直方向的模糊半径。

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

对 Images 进行模糊处理时,建议使用 BlurredEdgeTreatment(Shape)(而非 BlurredEdgeTreatment.Unbounded),因为后者用于对预计会在原始内容边界以外呈现的任意渲染进行模糊处理。图片很可能不会在内容边界以外呈现;而在对圆角矩形进行模糊处理时,可能需要这种不同的处理方式。

例如,在上图中,如果将 BlurredEdgeTreatment 设置为 Unbounded,则图片的边缘会显得模糊不清,而不会有清晰锐利的边缘:

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

上一篇 Jetpack Compose 动画(自定义动画)

下一篇 Jetpack Compose 图标