Как минимизировать количество рекомпозиций AsyncImage из Coli
Я пишу простой элемент, отображающий изображение и текст, а также имеющий функцию добавления элемента в избранное.
@Composable
fun ItemView(
item: Item,
modifier: Modifier = Modifier,
onFavoriteClick: (Item) -> Unit = {},
) {
Row(modifier = modifier) {
AsyncImage(
model = remember (item.id) {item.url},
contentDescription = "img",
contentScale = ContentScale.Crop,
placeholder = painterResource(id = R.drawable.restaurant_placeholder),
error = painterResource(id = R.drawable.restaurant_placeholder),
modifier = Modifier
.size(48.dp)
.clip(RoundedCornerShape(10)),
)
Text(text = item.name)
IconToggleButton(
checked = isFavourite,
onCheckedChange = remember { { onFavoriteClick(item) } }
) {
Icon(
imageVector = if (item.isFavourite) {
Icons.Filled.Favorite
} else {
Icons.Default.FavoriteBorder
},
contentDescription = null,
)
}
}
}
Я заметил, что ItemView не перекомпоновывается при добавлении в избранное (новый только значок), но когда я добавляю всю перекомпоновку, то и то.
Я пытался обернутьContentScale
иModifier
вспомнить, но всё равно не получилось.
AsyncImage(
model = remember (item.id) {item.url},
contentDescription = "img",
contentScale = remember {ContentScale.Crop},
placeholder = painterResource(id = R.drawable.placeholder),
error = painterResource(id = R.drawable.placeholder),
modifier = remember {Modifier
.size(48.dp)
.clip(RoundedCornerShape(10)) },
)
Также я заметил, что когда я удалилplaceholder
иerror
У меня на одну рекомпозицию меньше. Я думал, это нормально, чтоAsyncImage
будет перекомпонован при загрузке изображения, ноItemView
не должен. Что я делаю не так?
Я создаю эти элементы следующим образом (я думаю, что использование id в качестве ключа имеет больше смысла, чем хэш, чтобы не создавать новый элемент каждый раз при изменении его любимого статуса):
@OptIn(ExperimentalFoundationApi::class)
@Composable
fun MainView(vm: ItemViewModel) {
LazyColumn {
items(
count = vm.items.size,
key = { idx -> vm.items[idx].id },
) { idx ->
ItemView(
item = vm.items[idx],
modifier = remember { Modifier.animateItemPlacement() },
onFavoriteClick = remember { vm::onFavoriteClick },
)
}
}
}
1 ответ
Причина, по которой ItemView перекомпоновывается при использовании AsyncImage, заключается в том, что составной объект AsyncImage является составным объектом с отслеживанием состояния, что означает, что он создает свое собственное состояние и управляет им. Когда изображение завершает загрузку, составной элемент AsyncImage обновляет свое состояние, что вызывает перекомпоновку всего ItemView.
Чтобы избежать ненужных рекомпозиций, вы можете попробовать использовать функцию Remember для запоминания составного объекта AsyncImage, например:
val memoizedAsyncImage = remember(item.id, item.url) {
AsyncImage(
model = item.url,
contentDescription = "img",
contentScale = ContentScale.Crop,
placeholder = painterResource(id = R.drawable.restaurant_placeholder),
error = painterResource(id = R.drawable.restaurant_placeholder),
modifier = Modifier
.size(48.dp)
.clip(RoundedCornerShape(10)),
)
}
Row(modifier = modifier) {
memoizedAsyncImage()
// The rest of the code here
}
Это должно запомнить составной элемент AsyncImage, чтобы его не перекомпоновать без необходимости.
Что касается проблемы рекомпозиции избранного значка, вы можете попробовать обернуть IconToggleButton в функцию RememberUpdatedState, чтобы запомнить проверенное состояние кнопки переключения, например:
val checkedState = rememberUpdatedState(isFavourite)
IconToggleButton(
checked = checkedState.value,
onCheckedChange = remember { { onFavoriteClick(item) } }
) {
Icon(
imageVector = if (checkedState.value) {
Icons.Filled.Favorite
} else {
Icons.Default.FavoriteBorder
},
contentDescription = null,
)
}
Это должно гарантировать, что IconToggleButton будет перекомпоновываться только при изменении проверенного состояния.