You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

Jetpack Compose:解决LazyColumn项中图片重组后消失问题

修复LazyColumn中Card项重组时图片消失的问题

问题根源

你当前的PhotoUpload组件把图片Uri和Bitmap存在内部的remember状态里,而LazyColumn的列表项在滚出视图范围时会被销毁回收,重新滚回来时组件会重新创建,这时候remember保存的状态会丢失,导致图片消失。另外,LaunchedEffect用Unit作为key,只会执行一次,组件重建后即使Uri还存在,也不会重新加载Bitmap。

解决方案

方案一:状态提升(推荐)

把每个Card项的图片状态(Uri)移到LazyColumn的数据源中,让状态和列表项绑定,而不是存在组件内部。这样即使组件被销毁重建,也能从数据源获取到正确的Uri。

  1. 定义列表项数据类:
data class CardItem(val id: String, var imageUri: Uri? = null)
  1. 在LazyColumn所在的父组件中维护数据源:
val cardItems = remember { mutableStateListOf<CardItem>(
    CardItem("1"),
    CardItem("2"),
    // 更多列表项
) }

LazyColumn {
    items(cardItems, key = { it.id }) { item ->
        Card {
            PhotoUpload(
                currentUri = item.imageUri,
                onUriSelected = { newUri ->
                    // 更新数据源,触发重组
                    val index = cardItems.indexOf(item)
                    cardItems[index] = item.copy(imageUri = newUri)
                }
            )
        }
    }
}
  1. 修改PhotoUpload组件,接收外部状态和回调:
@Composable
fun PhotoUpload(
    currentUri: Uri?,
    onUriSelected: (Uri?) -> Unit
) {
    val context = LocalContext.current
    val bitmap = remember { mutableStateOf<Bitmap?>(null) }

    val launcher = rememberLauncherForActivityResult(
        contract = ActivityResultContracts.GetContent()
    ) { uri ->
        onUriSelected(uri)
    }

    // 以currentUri为key,Uri变化或组件重建时重新加载Bitmap
    LaunchedEffect(currentUri) {
        currentUri ?: run {
            bitmap.value = null
            return@LaunchedEffect
        }
        bitmap.value = if (Build.VERSION.SDK_INT < 28) {
            MediaStore.Images.Media.getBitmap(context.contentResolver, currentUri)
        } else {
            val source = ImageDecoder.createSource(context.contentResolver, currentUri)
            ImageDecoder.decodeBitmap(source)
        }
    }

    bitmap.value?.let { btm ->
        Image(
            bitmap = btm.asImageBitmap(),
            contentDescription = "上传的图片",
            modifier = Modifier.size(400.dp)
        )
    }

    Button(onClick = { launcher.launch("image/*") }) {
        Icon(Icons.Filled.PhotoAlbum, contentDescription = "选择图片")
    }
}

方案二:用rememberSaveable保存Uri

如果不想改动数据源,可以用rememberSaveable替代remember来保存Uri,它会将状态持久化到Bundle中,组件重建后能自动恢复状态。

修改后的PhotoUpload组件:

@Composable
fun PhotoUpload() {
    // 用rememberSaveable保存Uri,组件重建后不会丢失
    val imageUri = rememberSaveable { mutableStateOf<Uri?>(null) }
    val context = LocalContext.current
    val bitmap = remember { mutableStateOf<Bitmap?>(null) }

    val launcher = rememberLauncherForActivityResult(
        contract = ActivityResultContracts.GetContent()
    ) { uri ->
        imageUri.value = uri
    }

    // 以imageUri.value为key,Uri恢复后自动重新加载Bitmap
    LaunchedEffect(imageUri.value) {
        imageUri.value ?: run {
            bitmap.value = null
            return@LaunchedEffect
        }
        bitmap.value = if (Build.VERSION.SDK_INT < 28) {
            MediaStore.Images.Media.getBitmap(context.contentResolver, imageUri.value!!)
        } else {
            val source = ImageDecoder.createSource(context.contentResolver, imageUri.value!!)
            ImageDecoder.decodeBitmap(source)
        }
    }

    bitmap.value?.let { btm ->
        Image(
            bitmap = btm.asImageBitmap(),
            contentDescription = "上传的图片",
            modifier = Modifier.size(400.dp)
        )
    }

    Button(onClick = { launcher.launch("image/*") }) {
        Icon(Icons.Filled.PhotoAlbum, contentDescription = "选择图片")
    }
}

关于Coil的使用

如果用Coil的AsyncImage,直接传入Uri即可,不需要手动加载Bitmap,它会自动处理缓存和重建后的加载。只要Uri被正确保存(不管是状态提升还是rememberSaveable),就不会出现图片消失的问题:

AsyncImage(
    model = currentUri, // 或imageUri.value
    contentDescription = "上传的图片",
    modifier = Modifier.size(400.dp)
)

内容的提问来源于stack exchange,提问作者Paul S

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.08.09 19:31:15