Можно ли изменить размер компонуемого изображения без запуска рекомпозиции
у меня естьanimateDpAsState(..)
, всякий раз, когда эта анимация запускается, она изменяетModifier.size(value)
изImage(...)
тем самым вызывая рекомпозицию.
Есть ли способ пропустить фазу композиции для этого конкретного сценария? Разрешить изображению изменять свой размер?
2 ответа
Я нашел решение! Чтобы пропустить рекомпозицию, но все же повлиять на вещи вокруг макета, вы можете сделать это на этапе макета, в противном случае переместите его на этап рисования!
Примените этот модификатор к
Modifier.layout { measurable, constraints ->
val size = animationSize.toPx() // getting the size of the current animation
val placeable = measurable.measure(Constraints.fixed(size.toInt(), size.toInt())) // setting the actual constraints of the image
// Set the layout with the same width and height of the image.
// Inside the layout we will place the image. This layout function is like a "box"
layout(placeable.width,placeable.height) {
// And then we will place the image inside the "box"
placeable.placeRelative(0, 0)
}
}
Вы можете сделать это, используяModifier.drawWithContent
,Modifier.drawBeheind
или используя Canvas, которыйSpacer
сModifier.drawBehind
. Модификаторы с лямбда-триггеромLayout
,Layout->Draw
илиDraw
пропуск фазComposition
как в этом ответе .
Фрагмент ниже изменяет размер с анимацией, и если вы хотите, чтобы изменения размера применялись из центра, вы можете добавить перевод либо
@Composable
private fun ImageSizeAnimationSample() {
val painter = painterResource(id = R.drawable.landscape1)
var enabled by remember { mutableStateOf(true) }
val sizeDp by animateDpAsState(if (enabled) 200.dp else 0.dp)
val density = LocalDensity.current
val context = LocalContext.current
SideEffect {
println(" Composing...")
Toast.makeText(context, "Composing...", Toast.LENGTH_SHORT).show()
}
Canvas(modifier = Modifier.size(200.dp)) {
val dimension = density.run { sizeDp.toPx() }
with(painter) {
draw(size = Size(dimension, dimension))
}
}
Button(onClick = { enabled = !enabled }) {
Text("Enabled: $enabled")
}
}
С переводом
Canvas(modifier = Modifier.size(200.dp)) {
val dimension = density.run { sizeDp.toPx() }
with(painter) {
translate(left = (size.width - dimension) / 2, top = (size.height - dimension) / 2) {
draw(size = Size(dimension, dimension))
}
}
}
В этих примерах для анимации запускается только одна рекомпозиция, потому что
val sizeDp by animateDpAsState(if (enabled) 200.dp else 0.dp)
читает включенное значение, но вы можете обрабатывать анимацию с помощью Animatable, которая также не вызовет никакой рекомпозиции.
@Composable
private fun ImageSizeAnimationWithAnimatableSample() {
val painter = painterResource(id = R.drawable.landscape1)
val animatable = remember { Animatable(0f) }
val coroutineScope = rememberCoroutineScope()
val context = LocalContext.current
SideEffect {
println(" Composing...")
Toast.makeText(context, "Composing...", Toast.LENGTH_SHORT).show()
}
Canvas(modifier = Modifier.size(200.dp)) {
with(painter) {
val dimension = size.width * animatable.value
translate(left = (size.width - dimension) / 2, top = (size.height - dimension) / 2) {
draw(size = Size(dimension, dimension))
}
}
}
Button(onClick = {
coroutineScope.launch {
val value = animatable.value
if(value == 1f){
animatable.animateTo(0f)
}else {
animatable.animateTo(1f)
}
}
}) {
Text("Animate")
}
}