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