基础组件:Image
@Composable
@NonRestartableComposable
fun Image(
bitmap: ImageBitmap,//bitmap
contentDescription: String?,//无障碍内容描述
modifier: Modifier = Modifier,
alignment: Alignment = Alignment.Center,//摆放位置
contentScale: ContentScale = ContentScale.Fit,//缩放类型
alpha: Float = DefaultAlpha,//透明度
colorFilter: ColorFilter? = null,//颜色渲染
filterQuality: FilterQuality = DefaultFilterQuality
)
@Composable
fun Image(
painter: Painter,
contentDescription: String?,
modifier: Modifier = Modifier,
alignment: Alignment = Alignment.Center,
contentScale: ContentScale = ContentScale.Fit,
alpha: Float = DefaultAlpha,
colorFilter: ColorFilter? = null
)
@Composable
@NonRestartableComposable
fun Image(
imageVector: ImageVector,
contentDescription: String?,
modifier: Modifier = Modifier,
alignment: Alignment = Alignment.Center,
contentScale: ContentScale = ContentScale.Fit,
alpha: Float = DefaultAlpha,
colorFilter: ColorFilter? = null
)
调用ImageBitmap和ImageVector最终都会调用Painter的方法
缩放类型ContentScale
ContentScale.Fit//均匀缩放图片,并保持宽高比(默认)。如果内容小于指定大小,系统会放大图片以适应边界。
ContentScale.Crop //将图片居中裁剪到可用空间。
ContentScale.Inside //缩放来源图片,使宽高保持在目标边界内。如果来源图片在两个维度上都小于或等于目标,则其行为类似于“None”。内容始终包含在边界内。如果内容小于边界,则不会应用缩放。
ContentScale.FillWidth //缩放来源图片,保持宽高比不变,使边界与目标宽度匹配。
ContentScale.FillHeight //缩放来源图片,保持宽高比不变,使边界与目标高度匹配。
ContentScale.FillBounds //以非均匀方式垂直和水平缩放内容,以填充目标边界。(注意:如果将图片放入与其宽高比不完全相符的容器中,则图片会失真)
ContentScale.None //不对来源图片应用任何缩放。如果内容小于目标边界,则不会缩放以适应相应区域。
位置类型Alignment
Alignment.TopStart
Alignment.TopCenter
Alignment.TopEnd
Alignment.CenterStart
Alignment.Center
Alignment.CenterEnd
Alignment.BottomStart
Alignment.BottomCenter
Alignment.BottomEnd
1.加载本地图片资源
(1)加载本地图片资源
Image(
painter = painterResource(id = R.drawable.img1),
contentDescription = "这是加载本地的图片",
)
(2)加载本地图片资源bitmap
val bitmap = ImageBitmap.imageResource(id = R.drawable.img2)
Image(
bitmap = bitmap, contentDescription = "这是加载本地bitmap",
)
(3)加载ImageVector
val imageVector = ImageVector.vectorResource(id = R.drawable.ic_tab_project)
Image(
imageVector = imageVector,
contentDescription = "")
3.设置自定义图片裁剪、边框样式
(1)Image内部使用Modifier设置样式
Image(
painter = painterResource(id = R.drawable.img1),
modifier = Modifier
.size(160.dp, 90.dp)
.border(3.dp, color = Color.Yellow, RoundedCornerShape(10.dp))
.clip(RoundedCornerShape(10.dp)),
contentDescription = "这是加载本地的图片",
contentScale = ContentScale.Crop
)
(2)渐变边框样式
val rainbowColorsBrush = remember {
Brush.sweepGradient(
listOf(
Color(0xFF9575CD),
Color(0xFFBA68C8),
Color(0xFFE57373),
Color(0xFFFFB74D),
Color(0xFFFFF176),
Color(0xFFAED581),
Color(0xFF4DD0E1),
Color(0xFF9575CD)
)
)
}
Image(
painter = painterResource(id = R.drawable.img1),
modifier = Modifier
.size(200.dp)
.border(3.dp, rainbowColorsBrush, CircleShape)
.clip(CircleShape),
contentDescription = "这是加载本地的图片",
contentScale = ContentScale.Crop
)
(3)Image外部使用Surface
Surface(
shape = CircleShape,
border = BorderStroke(2.dp, Color.Green)
) {
Image(
painter = painterResource(id = R.drawable.img2), contentDescription = "",
modifier = Modifier.size(100.dp),
contentScale = ContentScale.Crop
)
}
(4)自定义裁剪路径
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.img2),
alignment = Alignment.Center,
contentScale = imageContentScale,//缩放类型,默认ContentScale.Fit
modifier = Modifier
.fillMaxWidth()
.height(200.dp)
.clip(SquashedOval()),
contentDescription = "这是加载本地的图片",
)
(5)设置自定义图片宽高比
Image(
painter = painterResource(id = R.drawable.img1),
contentDescription = "",
modifier = Modifier.aspectRatio(16f / 9f)
)
(6)颜色滤镜 - 转换图片的像素颜色
Image(
painter = painterResource(id = R.drawable.ic_tab_home),
contentDescription = "",
colorFilter = ColorFilter.tint(color = Color.Red, blendMode = BlendMode.SrcIn)
)
不同的BlendMode显示不同的效果
官网Api:“BlendMode”
Image(
painter = painterResource(id = R.drawable.img1),
contentDescription = "",
colorFilter = ColorFilter.tint(color = Color.Red, blendMode = BlendMode.Darken)
)
(7)通过颜色矩阵应用 Image 滤镜
4.自定义Painter加载
val customPainter = remember {
object : Painter() {
override val intrinsicSize: Size get() = Size(100.0f, 100.0f)
override fun DrawScope.onDraw() {
drawCircle(color = Color.Cyan)
}
}
}
Image(
painter = customPainter,
contentDescription = "",
modifier = Modifier.size(100.dp, 100.dp)
)
5.加载网络图片 Coil
添加依赖
implementation 'io.coil-kt.coil3:coil-compose:3.1.0'//compose
implementation 'io.coil-kt.coil3:coil-network-okhttp:3.1.0'//加载网络图片
implementation "io.coil-kt.coil3:coil-svg:3.1.0"//加载svg
(1)基础使用AsyncImage
AsyncImage是一个可组合组件,它异步执行图像请求并呈现结果。它支持与标准composable相同的参数,此外,它还支持设置 painter(placeholder/error/fallback)和回调(onLoading/onSuccess/onError)。
在大多数情况下更喜欢使用AsyncImage。它根据可组合的约束和提供的ContentScale正确地确定图像应该加载的大小。
AsyncImage(
modifier = Modifier
.size(160.dp, 90.dp)
.background(Color.LightGray),
model = "xxxxx图片url",
contentDescription = "这是加载网络图片",
contentScale = ContentScale.Crop
)
AsyncImage(
modifier = Modifier
.size(90.dp, 160.dp)
.background(Color.LightGray),
contentScale = ContentScale.Crop,
model = ImageRequest.Builder(LocalContext.current)
.data("xxxxx")
.crossfade(true)
.error(R.drawable.ic_error)
.build(),
contentDescription = "",
onState = { state ->//状态
onState = { state ->
when (state) {
is AsyncImagePainter.State.Loading -> {
}
is AsyncImagePainter.State.Error->{
}
is AsyncImagePainter.State.Empty->{
}
is AsyncImagePainter.State.Success->{
}
}
},
)
(2)使用rememberAsyncImagePainter
AsyncImage和SubcomposeAsyncImage内部使用rememberAsyncImagePainter加载model,如果需要的是一个Painter而不是composable组件,则可以使用rememberAsyncImagePainter去加载,并且可以观察AsyncImagePainter.state状态,根据不同状态显示不同的composable,或者可以手动的进行图像的重新加载(AsyncImagePainter.restart)
val painterOnly = rememberAsyncImagePainter(
"https://xxx.png"
)
Image(
painter = painterOnly,
contentDescription = "",
modifier = Modifier.size(160.dp,90.dp),
contentScale = ContentScale.Crop)
rememberAsyncImagePainter比AsyncImage和SubcomposeAsyncImage更灵活,但是也有缺点。
缺点1:
它不检测图像在屏幕上加载的大小,总是以其原始尺寸加载图像,可以通过使用自定义的SizeResolver或使用rememberConstraintsSizeResolver来解决。
val sizeResolver = rememberConstraintsSizeResolver()
val painter = rememberAsyncImagePainter(
model = ImageRequest.Builder(LocalPlatformContext.current)
.data("xxxxx.png")
.size(sizeResolver)
.build(),
)
Image(
painter = painter,
contentDescription = null,
modifier = Modifier.then(sizeResolver),
)
缺点2:
当使用rememberAsyncImagePainter时,即使图像在内存缓存中,它刚开始绘制时始终是AsyncImagePainter.State.Empty状态
(3)SubcomposeAsyncImage
SubcomposeAsyncImage是AsyncImage的变体,提供许多API,可以监听不同状态回调,还可以在不同状态显示不同的composable组件,但是它加载比普通的AsyncImage要慢,性能较差。
SubcomposeAsyncImage(
model = "xxx.jpg",
loading = {
CircularProgressIndicator()
},
error = {
Text(text = "出错了")
},
success = {
Image(painter = painterResource(id = R.drawable.ic_placeholder), contentDescription = "")
},
contentDescription = stringResource(R.string.description),
)
(5)订阅观察状态AsyncImagePainter.state
val painter = rememberAsyncImagePainter("https://xxx.png")
val state by painterForState.state.collectAsState()
when (state) {
is AsyncImagePainter.State.Empty,
is AsyncImagePainter.State.Loading -> {
CircularProgressIndicator()
}
is AsyncImagePainter.State.Success -> {
Image(
painter = painter,
contentDescription =""
)
}
is AsyncImagePainter.State.Error -> {
// Show some error UI.
}
}
(6)使用Transitions crossfade
自定义的Transitions 不能在AsyncImage、SubcomposeAsyncImage、rememberAsyncImagePainter中使用,因为他们需要一个View的引用,只是crossfade在内部做了支持。
AsyncImage(
model = ImageRequest.Builder(LocalContext.current)
.data("https://xxxx.jpg")
.crossfade(true)
.build(),
contentDescription = null,
)
(6)ImageLoader解析
imageloader是执行imagerequest的服务对象。它们处理缓存、数据获取、图像解码、请求管理、内存管理等等。当创建单个ImageLoader并在整个应用程序中共享它时,Coil表现最好。这是因为每个ImageLoader都有自己的内存缓存,磁盘缓存和OkHttpClient。
Coil3默认会创建一个单例的ImageLoader,可以通过多种方式进行配置
//setSafe方法确保它不会覆盖已创建的现有ImageLoader
val context= LocalContext.current
SingletonImageLoader.setSafe {
ImageLoader.Builder(context)
.crossfade(true)
.build()
}
//用于多平台
setSingletonImageLoaderFactory { context ->
ImageLoader.Builder(context)
.crossfade(true)
.build()
}
//在Android中,尽可能早的,可以在Application中创建
class App :Application(),SingletonImageLoader.Factory {
override fun onCreate() {
super.onCreate()
}
override fun newImageLoader(context: PlatformContext): ImageLoader {
return ImageLoader.Builder(context)
.crossfade(true)
.build()
}
}
ImageLoader缓存配置
每个ImageLoader都保留了最近解码的位图Bitmap的内存缓存,以及从互联网加载的任何图像的磁盘缓存。两者都可以在创建ImageLoader时配置。
class App : Application(), SingletonImageLoader.Factory {
override fun onCreate() {
super.onCreate()
}
override fun newImageLoader(context: PlatformContext): ImageLoader {
return ImageLoader.Builder(context)
.crossfade(true)
.memoryCache {
MemoryCache.Builder()
.maxSizePercent(context,0.25)
.build()
}
.diskCache {
DiskCache.Builder()
.directory(context.cacheDir.resolve("image_cache"))
.maxSizePercent(0.02)
.build()
}
.build()
}
}
(7)ImageRequests解析
ImageRequests为ImageLoader加载图像提供了所有必要的信息。
val imageRequest = ImageRequest.Builder(LocalContext.current)
.data("https://xxx.jpg")
.error(R.drawable.ic_error)
.diskCacheKey("Test_disk_cache_image")
.memoryCacheKey("Test_memory_cache_image")
.crossfade(true)
.build()
imageLoader.enqueue(imageRequest)