Jetpack Compose:解决LazyColumn项中图片重组后消失问题
修复LazyColumn中Card项重组时图片消失的问题
问题根源
你当前的PhotoUpload组件把图片Uri和Bitmap存在内部的remember状态里,而LazyColumn的列表项在滚出视图范围时会被销毁回收,重新滚回来时组件会重新创建,这时候remember保存的状态会丢失,导致图片消失。另外,LaunchedEffect用Unit作为key,只会执行一次,组件重建后即使Uri还存在,也不会重新加载Bitmap。
解决方案
方案一:状态提升(推荐)
把每个Card项的图片状态(Uri)移到LazyColumn的数据源中,让状态和列表项绑定,而不是存在组件内部。这样即使组件被销毁重建,也能从数据源获取到正确的Uri。
- 定义列表项数据类:
data class CardItem(val id: String, var imageUri: Uri? = null)
- 在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) } ) } } }
- 修改
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
相关产品推荐
相关产品推荐

