Я хотел бы, чтобы пользователь добавлял изображение к каждому элементу (карте) в 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, "")
}
}
Изображения удаляются (они не возвращаются, это зацикленный gif)
PS: Для LazyColumn я использую ключи. И я также пытался использовать AsyncImage из Coil, но у него была та же проблема.
Он удаляется, потому что, когда вы прокручиваете это загруженное изображение, LazyColumn удаляет элементы, которые больше не видны для повышения производительности, LazyList использует SubcomposeLayout под капотом, который перекомпоновывает видимые элементы и тот, который должен быть виден в направлении прокрутки, который невозможно, потому что вы не сохранили их где-то вне рекомпозиции (например, модель просмотра)!
Вы можете хранить Uris или Uris как строку внутри класса данных, такого как
@Immutable
data class MyModel(
val title: String,
val description: String,
val uri: Uri? = null
)
И создайте ViewModel с элементами и функцией для обновления 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 = rememberLauncherForActivityResult(
contract = ActivityResultContracts.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)
}
)
}
}
}
Результат
Обновление: оказалось, что я неправильно управлял своим списком, так как у меня было два списка с моим элементом, один в viewModel, а другой в моей компонуемой функции. Я исправил это, и теперь он работает правильно) хотя все еще немного лагает
Вы должны проверить фактическую производительность в режиме выпуска с помощью minifiedEnabled true
. И да, это не так хорошо, как RV с точки зрения производительности. Но используя mutableStateListOf
вместо mutableState<List<MyItem>, вы запускаете композицию только для одного элемента. Вы также можете проверить этот ответ stackoverflow.com/a/74700668/5457853
Спасибо за очень подробный ответ! Ваше решение помогло с исчезновением изображений. Но я столкнулся с 2 другими проблемами: изображения появляются только при перекомпоновке (поэтому изображения появляются только тогда, когда я прокручиваю куда-то и обратно), а lazyColumn стал вообще лагать. Вы сталкивались с этими проблемами?