加载图片,不同数据源,Compose实现

1.从图片资源文件中加载图片,提供resId。

less 复制代码
// We represent a Composable function by annotating it with the @Composable annotation. Composable
// functions can only be called from within the scope of other composable functions. We should
// think of composable functions to be similar to lego blocks - each composable function is in turn
// built up of smaller composable functions.
@Composable
fun LocalResourceImageComponent(@DrawableRes resId: Int) {
    // There are multiple methods available to load an image resource in Compose. However, it would
    // be advisable to use the painterResource method as it loads an image resource asynchronously
    val image = painterResource(resId)

    // Image is a pre-defined composable that lays out and draws a given [ImageBitmap].

    // You can think of Modifiers as implementations of the decorators pattern that are
    // used to modify the composable that its applied to. In this example, we configure the
    // Image composable to have a height of 200 dp.
    Image(
        painter = image,
        contentDescription = null,
        modifier = Modifier
            .sizeIn(maxHeight = 200.dp)
            .fillMaxWidth()
    )
}

2.根据网络URL地址,加载图片,使用Picasso图片加载框架

副作用函数,

这段代码用 DisposableEffect(url) 在 Compose 里做一次性副作用:当 url 变化时,用 Picasso 去加载图片,并在离开时清理。

要点:

  • DisposableEffect(url) 会在 首次进场 执行一次 block;当 url 改变 时,先调用之前那次的 onDispose{},再用新 url 重新执行 block;当 Composable 被移除 时也会调用 onDispose{}
  • 这里把 imperative 的图片加载 (Picasso)和 Compose 的 声明式状态image/drawable)桥接起来:回调里写入 mutableState → 触发 Compose 重组显示。
  • cancelRequest(target) 很重要,能在组件销毁或 url 切换时及时停止加载,防止内存泄漏或在"无效界面"上回调。

只要image和drawable的值发生变更,就会触发重组,在使用这两个状态变量的地方,就会重组显示。

csharp 复制代码
var image by remember { mutableStateOf<ImageBitmap?>(null) }
var drawable by remember { mutableStateOf<Drawable?>(null) }
kotlin 复制代码
DisposableEffect(url) {                       // 以 url 作为"键":url 变 → 先 dispose 旧的,再重建新的 effect
    val picasso = Picasso.get()
    val target = object : Target {            // 定义 Picasso 的回调对象 Target
        override fun onPrepareLoad(placeHolderDrawable: Drawable?) {
            drawable = placeHolderDrawable    // 显示占位图(外层的 mutableState)
        }
        override fun onBitmapFailed(e: Exception?, errorDrawable: Drawable?) {
            drawable = errorDrawable          // 失败图(外层的 mutableState)
        }
        override fun onBitmapLoaded(bitmap: Bitmap?, from: Picasso.LoadedFrom?) {
            image = bitmap?.asImageBitmap()   // 成功后把 Bitmap 写到 Compose 状态
        }
    }
    picasso
        .load(url)
        .into(target)                         // 发起网络/磁盘加载(副作用)

    onDispose {                               // 清理阶段(url 变或 Composable 离开 Composition)
        image = null
        drawable = null
        picasso.cancelRequest(target)         // 取消请求,避免泄漏/无用回调
    }
}
kotlin 复制代码
// We represent a Composable function by annotating it with the @Composable annotation. Composable
// functions can only be called from within the scope of other composable functions. We should
// think of composable functions to be similar to lego blocks - each composable function is in turn
// built up of smaller composable functions.
@Composable
fun NetworkImageComponentPicasso(
    url: String,
    modifier: Modifier = Modifier
) {
    // Source code inspired from - https://kotlinlang.slack.com/archives/CJLTWPH7S/p1573002081371500.
    // Made some minor changes to the code Leland posted.
    val sizeModifier = modifier
        .fillMaxWidth()
        .sizeIn(maxHeight = 200.dp)
    var image by remember { mutableStateOf<ImageBitmap?>(null) }
    var drawable by remember { mutableStateOf<Drawable?>(null) }
    // Sometimes we need to make changes to the state of the app. For those cases, Composes provides
    // some Effect API's which provide a way to perform side effects in a predictable manner.
    // DisposableEffect is one such side effect API that provides a mechanism to perform some
    // clean up actions if the key to the effect changes or if the composable leaves composition.
    DisposableEffect(url) {
        val picasso = Picasso.get()
        val target = object : Target {
            override fun onPrepareLoad(placeHolderDrawable: Drawable?) {
                // TODO(lmr): we could use the drawable below
                drawable = placeHolderDrawable
            }

            override fun onBitmapFailed(e: Exception?, errorDrawable: Drawable?) {
                drawable = errorDrawable
            }

            override fun onBitmapLoaded(bitmap: Bitmap?, from: Picasso.LoadedFrom?) {
                image = bitmap?.asImageBitmap()
            }
        }
        picasso
            .load(url)
            .into(target)
        onDispose {
            image = null
            drawable = null
            picasso.cancelRequest(target)
        }
    }

    val theImage = image
    val theDrawable = drawable
    if (theImage != null) {
        // Column is a composable that places its children in a vertical sequence. You
        // can think of it similar to a LinearLayout with the vertical orientation. 
        // In addition we also pass a few modifiers to it.

        // You can think of Modifiers as implementations of the decorators pattern that are
        // used to modify the composable that its applied to. 
        Column(
            modifier = sizeModifier,
            verticalArrangement = Arrangement.Center,
            horizontalAlignment = Alignment.CenterHorizontally
        ) {
            // Image is a pre-defined composable that lays out and draws a given [ImageBitmap].
            Image(bitmap = theImage, contentDescription = null)
        }
    } else if (theDrawable != null) {
        // We use the Canvas composable that gives you access to a canvas that you can draw
        // into. We also pass it a modifier.
        Canvas(modifier = sizeModifier) {
            drawIntoCanvas { canvas ->
                theDrawable.draw(canvas.nativeCanvas)
            }
        }
    }
}

3.根据图片URL地址,加载图片,使用Glide图片加载框架实现

kotlin 复制代码
@Composable
fun NetworkImageComponentGlide(
    url: String, modifier: Modifier = Modifier
) {
    // Reacting to state changes is the core behavior of Compose. You will notice a couple new
    // keywords that are compose related - remember & mutableStateOf.remember{} is a helper
    // composable that calculates the value passed to it only during the first composition. It then
    // returns the same value for every subsequent composition. Next, you can think of
    // mutableStateOf as an observable value where updates to this variable will redraw all
    // the composable functions that access it. We don't need to explicitly subscribe at all. Any
    // composable that reads its value will be recomposed any time the value
    // changes. This ensures that only the composables that depend on this will be redraw while the
    // rest remain unchanged. This ensures efficiency and is a performance optimization. It
    // is inspired from existing frameworks like React.
    var image by remember { mutableStateOf<ImageBitmap?>(null) }
    var drawable by remember { mutableStateOf<Drawable?>(null) }
    val sizeModifier = modifier
        .fillMaxWidth()
        .sizeIn(maxHeight = 200.dp)

    // LocalContext is a LocalComposition for accessting the context value that we are used to using
    // in Android.

    // LocalComposition is an implicit way to pass values down the compose tree. Typically, we pass values
    // down the compose tree by passing them as parameters. This makes it easy to have fairly
    // modular and reusable components that are easy to test as well. However, for certain types
    // of data where multiple components need to use it, it makes sense to have an implicit way
    // to access this data. For such scenarios, we use LocalComposition. In this example, we use the
    // LocalContext to get hold of the Context object. In order to get access to the latest
    // value of the LocalComposition, use the "current" property eg - LocalContext.current. Some other
    // examples of common LocalComposition's are LocalTextInputService,LocalDensity, etc.
    val context = LocalContext.current
    // Sometimes we need to make changes to the state of the app. For those cases, Composes provides
    // some Effect API's which provide a way to perform side effects in a predictable manner.
    // DisposableEffect is one such side effect API that provides a mechanism to perform some
    // clean up actions if the key to the effect changes or if the composable leaves composition.
    DisposableEffect(url) {
        val glide = Glide.with(context)
        val target = object : CustomTarget<Bitmap>() {
            override fun onLoadCleared(placeholder: Drawable?) {
                image = null
                drawable = placeholder
            }

            override fun onResourceReady(bitmap: Bitmap, transition: Transition<in Bitmap>?) {
                image = bitmap.asImageBitmap()
            }
        }
        glide
            .asBitmap()
            .load(url)
            .into(target)

        onDispose {
            image = null
            drawable = null
            glide.clear(target)
        }
    }

    val theImage = image
    val theDrawable = drawable
    if (theImage != null) {
        // Column is a composable that places its children in a vertical sequence. You
        // can think of it similar to a LinearLayout with the vertical orientation. 
        // In addition we also pass a few modifiers to it.

        // You can think of Modifiers as implementations of the decorators pattern that are
        // used to modify the composable that its applied to.
        Column(
            modifier = sizeModifier,
            verticalArrangement = Arrangement.Center,
            horizontalAlignment = Alignment.CenterHorizontally
        ) {
            // Image is a pre-defined composable that lays out and draws a given [ImageBitmap].
            Image(bitmap = theImage, contentDescription = null)
        }
    } else if (theDrawable != null) {
        // We use the Canvas composable that gives you access to a canvas that you can draw
        // into. We also pass it a modifier.
        Canvas(modifier = sizeModifier) {
            drawIntoCanvas { canvas ->
                theDrawable.draw(canvas.nativeCanvas)
            }
        }
    }
}

4.加载图片,并且图片是有圆角的:

less 复制代码
@Composable
fun ImageWithRoundedCorners(@DrawableRes resId: Int) {
    // There are multiple methods available to load an image resource in Compose. However, it would
    // be advisable to use the painterResource method as it loads an image resource asynchronously
    val image = painterResource(resId)
    // Column is a composable that places its children in a vertical sequence. You
    // can think of it similar to a LinearLayout with the vertical orientation.
    // In addition we also pass a few modifiers to it.

    // You can think of Modifiers as implementations of the decorators pattern that are
    // used to modify the composable that its applied to. In this example, we configure the
    // Box composable to clip the corners of the image.
    Column(
        modifier = Modifier.clip(RoundedCornerShape(8.dp))
    ) {
        // Image is a pre-defined composable that lays out and draws a given [ImageBitmap].
        Image(
            painter = image,
            modifier = Modifier.height(200.dp),
            contentDescription = null
        )
    }
}
相关推荐
OpenTiny社区3 小时前
如何使用 TinyEditor 快速部署一个协同编辑器?
前端·vue.js
Mike_jia3 小时前
Dumbterm:基于网页的终端革命!随时随地安全访问服务器的终极方案
前端
看今朝·3 小时前
【Dash框架】Dash回调函数中Output的属性详解
java·前端·dash
Data_Adventure3 小时前
文件Base64转换工具升级:从图片到多格式文件的全新体验
前端
D11_3 小时前
【React】验证码图片管理系统
前端
掘金安东尼4 小时前
Caddyfile:用最简单的方式配置最现代的 Web 服务器
运维·服务器·前端
菠萝+冰4 小时前
React-Window 虚拟化滚动
前端·react.js·前端框架
皓月Code4 小时前
第三章、React项目国际化介绍(`react-i18next`)
前端·javascript·react.js·1024程序员节
云中雾丽4 小时前
react中 所有给 children 传值的方式
前端