我想让用户添加图像到每个项目(卡)在LazyColumn.但似乎图像在重新构图时被删除了.我怎么才能解决这个问题呢?

@Composable
fun PhotoUpload(
) {
    val imageUri = remember {
        mutableStateOf<Uri?>(null)
    }
    val context = LocalContext.current
    val bitmap = remember {
        mutableStateOf<Bitmap?>(null)
    }

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

    imageUri.value?.let {
        LaunchedEffect(Unit) {
            if (Build.VERSION.SDK_INT < 28) {
                bitmap.value = MediaStore.Images
                    .Media.getBitmap(context.contentResolver, it)
            } else {
                val source = ImageDecoder
                    .createSource(context.contentResolver, it)
                bitmap.value = ImageDecoder.decodeBitmap(source)
            }
        }
    }

    bitmap.value?.let { btm ->
        Image(
            bitmap = btm.asImageBitmap(),
            contentDescription = null,
            modifier = Modifier.size(400.dp)
        )
    }

    Button(onClick = {
        launcher.launch("image/*")
    }) {
        Icon(Icons.Filled.PhotoAlbum, "")
    }
}

example

图像被删除(它们不会回来,这是循环gif)

PS:对于LazyColumn,我确实使用了键.我也试过使用Coil的AsyncImage,但它也有同样的问题

推荐答案

它被删除是因为当你滚动离开上传的图像时,LazyColumn会删除不再可见的项目以获得更好的性能,LazyList在引擎盖下使用子组合布局,它重新组合了可见的项目和即将在滚动方向上可见的项目,这是不可能的,因为你没有将它们保存在重组的某个地方(例如视图模型)!

您可以将URI或URI作为字符串存储在数据类中,例如

@Immutable
data class MyModel(
    val title: String,
    val description: String,
    val uri: Uri? = null
)

并创建一个ViewModel,其中包含项和在设置URI时更新URI的函数

class MyViewModel : ViewModel() {
    val items = mutableStateListOf<MyModel>()
        .apply {
            repeat(15) {
                add(MyModel(title = "Title$it", description = "Description$it"))
            }
        }

    fun update(index: Int, uri: Uri) {
        val item = items[index].copy(uri = uri)
        items[index] = item
    }
}

通过点击按钮获取URI

@Composable
fun PhotoUpload(
    onError: (() -> Unit)? = null,
    onImageSelected: (Uri) -> Unit
) {

    val launcher = rememberLauncherForActivity结果(
        contract = Activity结果Contracts.GetContent()
    ) { uri: Uri? ->
        if (uri == null) {
            onError?.invoke()
        } else {
            onImageSelected(uri)
        }

    }

    Button(onClick = {
        launcher.launch("image/*")
    }) {
        Icon(Icons.Filled.PhotoAlbum, "")
    }
}

设置后通过回调返回URI的行

@Composable
private fun MyRow(
    title: String,
    description: String,
    uri: Uri?,
    onImageSelected: (Uri) -> Unit
) {
    Column(
        modifier = Modifier
            .shadow(ambientColor = Color.LightGray, elevation = 4.dp)
            .fillMaxWidth()
            .background(Color.White, RoundedCornerShape(8.dp))
            .padding(8.dp)
    ) {
        Text(title)
        Text(description)
        uri?.let { imageUri ->
            AsyncImage(
                modifier = Modifier
                    .fillMaxWidth()
                    .aspectRatio(4 / 3f),
                model = imageUri, contentDescription = null
            )
        }
        PhotoUpload(onImageSelected = onImageSelected)
    }
}

用法

@Composable
private fun ImageUploadSample(viewModel: MyViewModel) {
    LazyColumn {

        itemsIndexed(
            key = { _, item ->
                item.hashCode()
            },
            items = viewModel.items
        ) { index, myModel ->
            MyRow(
                title = myModel.title,
                description = myModel.description,
                uri = myModel.uri,
                onImageSelected = { uri ->
                    viewModel.update(index, uri)
                }
            )
        }
    }
}

结果

enter image description here

Android相关问答推荐

如何在Android Emulator上从物理设备接收TCP消息

Android Gradle/Groovy,如何将文件复制到APK

在以XML格式设置完整屏幕视图时可见App Compat按钮

Android在NavHost中的LazyColumn中编写约束布局:error - replace()在未放置的项目上调用

如何从URI中获取图像大小

OutlinedTextField仅显示一次

Play Google上发布的一款应用的房间数据库迁移

AndroidX Media3 迁移指南

Jetpack Compose X.dp 性能问题?

Android Studio Relay插件(版本0.3.07)错误

为什么我收到这个错误我需要安装 android studio

如何在没有人窃取令牌的情况下使用我的移动应用程序中的 API

PayUCheckoutPro Android SDK 实现问题

如何只允许拖动 BottomSheetScaffold 中 BottomContent 的 SheetPeek 的一部分?

如何在 BasicTextField 中全选焦点

在 Android Studio 中替换字符串中的 "

如何将房间数据库导出到 .CSV

Android YouTube SDK - 视频无法在某些 Android 设备中播放

我可以在不解密的情况下使用 JSch 获取加密的 SSH 私钥的类型或 fingerprint 吗?

自定义布局忽略可组合的大小