基础组件:Image简单使用和Coil的AsyncImage(一)

基础组件: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)
### Jetpack Compose 设置背景图片方法 在 Jetpack Compose 中,可以通过 `Box` 或其他布局容器来实现背景图片的设置。以下是具体实现方法: #### 使用 Box Image 组件 通过组合 `Box` `Image` 组件可以轻松实现背景图片的效果。以下是个完整的代码示例[^1]: ```kotlin import androidx.compose.foundation.Image import androidx.compose.foundation.layout.Box import androidx.compose.foundation.layout.fillMaxSize import androidx.compose.runtime.Composable import androidx.compose.ui.Modifier import coil.compose.rememberAsyncImagePainter @Composable fun BackgroundImageExample() { val painter = rememberAsyncImagePainter(model = "https://example.com/background.jpg") Box(modifier = Modifier.fillMaxSize()) { Image( painter = painter, contentDescription = "Background Image", modifier = Modifier.fillMaxSize() ) // 可在此处添加其他 UI 元素 } } ``` 上述代码中使用Coil 库加载网络图片作为背景图像[^2]。如果需要本地资源文件,则可以直接替换为 `painterResource(id = R.drawable.background)`。 #### 自定义修饰符 (Modifier)种更灵活的方式是自定义个扩展函数,在其中封装背景图片逻辑: ```kotlin import androidx.compose.foundation.layout.Box import androidx.compose.foundation.layout.fillMaxSize import androidx.compose.runtime.Composable import androidx.compose.ui.Modifier import androidx.compose.ui.graphics.ColorFilter import androidx.compose.ui.platform.LocalContext import androidx.core.content.ContextCompat import coil.compose.AsyncImage fun Modifier.backgroundImage(resId: Int): Modifier = this.then( Modifier.background( AsyncImage( model = resId, contentDescription = null, colorFilter = ColorFilter.tint(ContextCompat.getColor(LocalContext.current, resId)) ).modifier ) ) @Composable fun CustomBackgroundExample() { Box( modifier = Modifier .fillMaxSize() .backgroundImage(R.drawable.background_image) ) { // 添加更多UI组件... } } ``` 此方法允许您将复杂的背景处理抽象化并重复利用[^3]。 #### 注意事项 - 如果要支持深色模式,请确保提供两种版本的图片或者调整颜色过滤器以适应不同主题。 - 对于大尺寸高分辨率图片可能会影响性能,建议优化图片大小格式[^4]。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值