阅读本文需要一定compose基础,如果没有请移步
本文介绍Compose中Image控件的基本概念以及深入了解。根据官网教程总结,如有不对请在评论区指教
Image(){} 是一个类似xml布局中 imageView的控件,主要用于展示图片,它包含了如下属性参数
一般使用代码示例:
Image(
painter = painterResource(id = R.mipmap.girl) ,
contentDescription = "小姐姐"
)
效果如下
上面的图片非常漂亮,但是在实际开发app中很多时候我们并不需要展示精美切像素那么高的位图,我们持续加载超过所需大小的图片可能会耗尽 GPU 缓存,从而导致界面渲染性能不佳。从而导致OutOfMemoryError;在这一点上,xml中的imageView和Compose中的Image都有这个问题,Image则可以通过contentScale属性来进行内容缩放和裁剪解决,不得不说的是,三方库在这个问题的解决上比原生控件更优秀
为了让读者更易懂contentScale内容缩放,我按照官方示例增加了图片的背景和边框,并且并排展示纵向和横向的图片
均匀缩放图片,并保持宽高比。如果内容小于指定大小,系统会放大图片以适应边界。
代码示例
Row (
Modifier.background(White),
horizontalArrangement = Arrangement.Center ,
verticalAlignment = Alignment.CenterVertically
){
val imageModifier = Modifier
.size(150.dp)
.border(BorderStroke(1.dp, Black))
.background(Yellow)
Column {
Image(
painter = painterResource(id = R.mipmap.girl) ,
contentDescription = "小姐姐",
modifier = imageModifier
)
}
Column(
Modifier.padding(start = 10.dp)
) {
Image(
painter = painterResource(id = R.mipmap.girl1) ,
contentDescription = "小姐姐1",
modifier = imageModifier
)
}
}
效果如下
这个时候图片等比缩小了,对比一开始的图片我们可以发现图片还是完整展示的
代码如下
val imageModifier = Modifier
.size(150.dp)
.border(BorderStroke(1.dp, Black))
.background(Yellow)
val contentScale = ContentScale.Crop
Row (
Modifier.background(White),
horizontalArrangement = Arrangement.Center ,
verticalAlignment = Alignment.CenterVertically
){
Column {
Image(
painter = painterResource(id = R.mipmap.girl) ,
contentDescription = "小姐姐",
modifier = imageModifier,
contentScale = contentScale
)
}
Column(
Modifier.padding(start = 10.dp)
) {
Image(
painter = painterResource(id = R.mipmap.girl1) ,
contentDescription = "小姐姐1",
modifier = imageModifier,
contentScale = contentScale
)
}
}
效果如下
因为下面的所有代码示例只替换了 val contentScale 中的值,故后面的属性都只展示和描述效果
效果如下
效果如下
用我个人的话来理解就是不裁剪图片并将图片拉伸至填充满整个Image控件,这样既可以完全展示图片,有让其填充满控件,缺点是图片容易失真,看起来贼丑
效果如下
缩放来源图片,使宽高保持在目标边界内。如果来源图片在两个维度上都小于或等于目标,则其行为类似于“None”。内容始终包含在边界内。如果内容小于边界,则不会应用缩放。
效果如下
效果如下
因为原图比较大,所以不缩放就成这个样子了
这个其实归功于修饰符的属性
Modifier .clip()
这个属性呢,主要是将图片或裁剪成圆形或者圆角的,之前的文章中也提到过将背景裁剪成圆角和圆形的大概使用方法为
Modifier
.height(167.dp)
.background(
colorResource(R.color.color_f5f5f5),
shape = RoundedCornerShape(16.dp, 16.dp, 0.dp, 0.dp)
)
将上例子中的contentScale去掉,并修改imageModifier为如下
val imageModifier = Modifier
.size(150.dp)
//加了边框
.border(BorderStroke(1.dp, Black))
.background(Yellow)
.clip( RoundedCornerShape(16.dp, 16.dp, 0.dp, 0.dp))
正当我自信满满的时候,代码没有生效!尬住了,仔细查看后,原来是因为默认的缩放属性ContentScale.Fit自适应边界导致的,于是我将缩放属性换为ContentScale.Crop,代码生效了,为了体现效果我去掉了图二的contentScale
代码如下
val imageModifier = Modifier
.size(150.dp)
.border(BorderStroke(1.dp, Black))
.background(Yellow)
.clip( RoundedCornerShape(16.dp, 16.dp, 0.dp, 0.dp))
val contentScale =ContentScale.Crop
Row (
Modifier.background(White),
horizontalArrangement = Arrangement.Center ,
verticalAlignment = Alignment.CenterVertically
){
Column {
Image(
painter = painterResource(id = R.mipmap.girl) ,
contentDescription = "小姐姐",
modifier = imageModifier,
contentScale = contentScale
)
}
Column(
Modifier.padding(start = 10.dp)
) {
Image(
painter = painterResource(id = R.mipmap.girl1) ,
contentDescription = "小姐姐1",
modifier = imageModifier,
)
}
}
效果如下
代码如下
val rainbowColorsBrush = remember {
Brush.sweepGradient(
listOf(
Color(0xFF9575CD),
Color(0xFFBA68C8),
Color(0xFFE57373),
Color(0xFFFFB74D),
Color(0xFFFFF176),
Color(0xFFAED581),
Color(0xFF4DD0E1),
Color(0xFF9575CD)
)
)
}
val imageModifier = Modifier
.size(150.dp)
.border(
BorderStroke(4.dp, rainbowColorsBrush),
RoundedCornerShape(16.dp, 16.dp, 0.dp, 0.dp)
)
.background(Yellow)
.clip( RoundedCornerShape(16.dp, 16.dp, 0.dp, 0.dp))
val contentScale =ContentScale.Crop
Row (
Modifier.background(White),
horizontalArrangement = Arrangement.Center ,
verticalAlignment = Alignment.CenterVertically
){
Column {
Image(
painter = painterResource(id = R.mipmap.girl) ,
contentDescription = "小姐姐",
modifier = imageModifier,
contentScale = contentScale
)
}
Column(
Modifier.padding(start = 10.dp)
) {
Image(
painter = painterResource(id = R.mipmap.girl1) ,
contentDescription = "小姐姐1",
modifier = imageModifier,
)
}
}
效果如下
到了这一步,可以看到边框和背景还有图片,是三种不同的体现,可以对比差别
修改imageModifier 代码如下
val imageModifier = Modifier
.size(150.dp)
.border(
BorderStroke(4.dp, rainbowColorsBrush),
RoundedCornerShape(16.dp, 16.dp, 0.dp, 0.dp)
)
.background(Yellow)
.clip( RoundedCornerShape(16.dp, 16.dp, 0.dp, 0.dp))
//图片转换为自定义宽高比
.aspectRatio(16f / 9f)
效果如下
这一功能主要是通过colorFilter 方法实现,colorFilter 的基本用法,引用官方示例如下
Image(
painter = painterResource(id = R.drawable.dog),
contentDescription = stringResource(id = R.string.dog_content_description),
colorFilter = ColorFilter.tint(Color.Green, blendMode = BlendMode.Darken)
)
可以看到主要是ColorFilter.tint()方法,我们看看它的源码
可以看到需要我们传入一个Color颜色,然后传入一个BlendMode(颜色混合的模式【自己的理解】),默认是BlendMode.SrcIn,我们通过官方文档可以看到有如下表格那么多种模式:
混合模式参数 | 效果 |
---|---|
BlendMode.Color | 获取源图像的色调和饱和度,以及目标图像的亮度。 |
BlendMode.ColorBurn | 将目标的倒数除以源,然后求结果的倒数。 |
BlendMode.ColorDodge | 将目标的倒数除以源。 |
BlendMode.Darken | 通过从每个颜色通道中选择最低值来合成源图像和目标图像。 |
BlendMode.Difference | 从每个通道的较大值中减去较小值。 |
BlendMode.Dst | 删除源图像,仅绘制目标图像。 |
BlendMode.DstAtop | 在源图像上合成目标图像,但仅在其与源图像重叠的位置。 |
BlendMode.DstIn | 显示目标图像,但仅显示两个图像重叠的位置。 |
BlendMode.DstOut | 显示目标图像,但仅在两个图像不重叠的地方显示。 |
BlendMode.DstOver | 在目标图像下合成源图像 |
BlendMode.Exclusion | 从两幅图像的总和中减去两幅图像乘积的两倍。 |
BlendMode.Hardlight | 在调整源图像和目标图像的分量以利于源图像之后,将它们相乘。 |
BlendMode.Hue | 获取源图像的色调,以及目标图像的饱和度和亮度。 |
BlendMode.Lighten | 通过从每个颜色通道中选择最高值来合成源图像和目标图像。 |
BlendMode.Luminosity | 获取源图像的亮度,以及目标图像的色调和饱和度。 |
BlendMode.Modulate | 将源图像和目标图像的颜色分量相乘。 |
BlendMode.Multiply | 将源图像和目标图像的分量相乘,包括alpha通道。 |
BlendMode.Overlay | 在调整源图像和目标图像的分量以有利于目标之后,将它们相乘。 |
BlendMode.Plus | 将源图像和目标图像的分量相加。 |
BlendMode.Saturation | 获取源图像的饱和度以及目标图像的色调和亮度。 |
BlendMode.Screen | 将源图像和目标图像的分量的倒数相乘,并求出结果的倒数。 |
BlendMode.Softlight | 对于低于0.5的源值,使用ColorDodge;对于高于0.5的源,使用ColorBurn。 |
BlendMode.Src | 删除目标图像,仅绘制源图像。 |
BlendMode.SrcAtop | 在目标图像上合成源图像,但仅在其与目标重叠的位置。 |
BlendMode.SrcIn | 显示源图像,但仅显示两个图像重叠的位置。 |
BlendMode.SrcOut | 显示源图像,但仅在两个图像不重叠的地方显示。 |
BlendMode.SrcOver | 在目标图像上合成源图像。 |
BlendMode.Xor | 对源图像和目标图像应用逐位异或运算符。 |
读者可以根据自己的需求选用参数
使用如下代码为图片添加黑白滤镜
colorFilter = ColorFilter.colorMatrix(ColorMatrix().apply { setToSaturation(0f) })
效果如下
更改对比度或亮度
val rainbowColorsBrush = remember {
Brush.sweepGradient(
listOf(
Color(0xFF9575CD),
Color(0xFFBA68C8),
Color(0xFFE57373),
Color(0xFFFFB74D),
Color(0xFFFFF176),
Color(0xFFAED581),
Color(0xFF4DD0E1),
Color(0xFF9575CD)
)
)
}
val imageModifier = Modifier
.size(150.dp)
.border(
BorderStroke(4.dp, rainbowColorsBrush),
RoundedCornerShape(16.dp, 16.dp, 0.dp, 0.dp)
)
.background(Yellow)
.clip( RoundedCornerShape(16.dp, 16.dp, 0.dp, 0.dp))
.aspectRatio(16f / 9f)
val contentScale =ContentScale.Crop
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
)
Row (
Modifier.background(White),
horizontalArrangement = Arrangement.Center ,
verticalAlignment = Alignment.CenterVertically
){
Column {
Image(
painter = painterResource(id = R.mipmap.girl) ,
contentDescription = "小姐姐",
modifier = imageModifier,
contentScale = contentScale,
//更改对比度或亮度
colorFilter = ColorFilter.colorMatrix(ColorMatrix(colorMatrix))
)
}
Column(
Modifier.padding(start = 10.dp)
) {
Image(
painter = painterResource(id = R.mipmap.girl1) ,
contentDescription = "小姐姐1",
modifier = imageModifier,
)
}
}
效果如下
模糊效果
更改 imageModifier 代码如下
val imageModifier = Modifier
.size(150.dp)
.border(
BorderStroke(4.dp, rainbowColorsBrush),
RoundedCornerShape(16.dp, 16.dp, 0.dp, 0.dp)
)
.background(Yellow)
.clip( RoundedCornerShape(16.dp, 16.dp, 0.dp, 0.dp))
.aspectRatio(16f / 9f)
//模糊效果
.blur(
radiusX = 10.dp,
radiusY = 10.dp,
edgeTreatment = BlurredEdgeTreatment(RoundedCornerShape(8.dp))
)
效果如下
val rainbowColorsBrush = remember {
Brush.sweepGradient(
listOf(
Color(0xFF9575CD),
Color(0xFFBA68C8),
Color(0xFFE57373),
Color(0xFFFFB74D),
Color(0xFFFFF176),
Color(0xFFAED581),
Color(0xFF4DD0E1),
Color(0xFF9575CD)
)
)
}
val imageModifier = Modifier
.size(150.dp)
.border(
BorderStroke(4.dp, rainbowColorsBrush),
RoundedCornerShape(16.dp, 16.dp, 0.dp, 0.dp)
)
.background(Yellow)
.clip( RoundedCornerShape(16.dp, 16.dp, 0.dp, 0.dp))
.aspectRatio(16f / 9f)
.blur(
radiusX = 10.dp,
radiusY = 10.dp,
edgeTreatment = BlurredEdgeTreatment(RoundedCornerShape(8.dp))
)
val contentScale =ContentScale.Crop
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
)
Row (
Modifier.background(White),
horizontalArrangement = Arrangement.Center ,
verticalAlignment = Alignment.CenterVertically
){
Column {
Image(
painter = painterResource(id = R.mipmap.girl) ,
contentDescription = "小姐姐",
modifier = imageModifier,
contentScale = contentScale,
colorFilter = ColorFilter.colorMatrix(ColorMatrix(colorMatrix))
)
}
Column(
Modifier.padding(start = 10.dp)
) {
Image(
painter = painterResource(id = R.mipmap.girl1) ,
contentDescription = "小姐姐1",
modifier = imageModifier,
)
}
}
添加依赖
implementation "io.coil-kt:coil-compose:2.1.0"
使用
@Composable
fun NetworkImage(){
val url = "https://img-blog.csdnimg.cn/6343e1698dc34686b87dbf50f4eaf0f2.png"
Column(
Modifier.padding(top = 50.dp)
){
AsyncImage(
model = url,
contentDescription = null
)
val modelBuilder = ImageRequest.Builder(LocalContext.current)
.data(url ?: "")
.crossfade(false)
.allowHardware(true)
.build()
Image(
painter = rememberAsyncImagePainter(
model = modelBuilder
),
contentDescription = "头像",
)
}
}
参考