#android #kotlin #android-jetpack-compose #jetpack-compose-animation
Вопрос:
У меня есть экран с изображением в одном углу экрана, и я хочу анимировать его в центре экрана. Что-то вроде перехода от
Icon(
painter = //,
contentDescription = //,
modifier = Modifier.size(36.dp)
)
Для
Icon(
painter = //,
contentDescription = //,
modifier = Modifier.fillMaxSize()
)
Первый находится в верхнем левом углу экрана, а второй — в центре. Как я могу анимировать между двумя состояниями?
Комментарии:
1. Ну, если он должен занимать всю ширину экрана, вам просто нужно анимировать размер значка. Он будет автоматически центрирован.
Ответ №1:
Попробуйте вот это:
@Composable
fun DUM_E_MARK_II(triggered: Boolean) {
BoxWithConstraints {
val size by animateDpAsState(if (triggered) 36.dp else maxHeight)
Icon(
imageVector = Icons.Filled.Warning,
contentDescription = "Just a better solution to the problem",
modifier = Modifier.size(size)
)
}
}
Комментарии:
1. GIF немного отстает, но это ошибка при записи. Сама анимация довольно плавная.
2. Итак, что заставляет вас думать, что это неправильное решение? Это более эффективно по сравнению с отмеченным ответом.
3. Это более простое решение. Спасибо!
4. На всякий случай, если кому-то нужна максимальная высота экрана для этого
maxHeight
параметра, вы можете получить его, вызвавLocalConfiguration.current.screenHeightDp
в пределах области композиции, подумал, что это может помочь.
Ответ №2:
Чтобы заставить анимацию работать в Compose, вам нужно анимировать значение определенного модификатора. Вы не можете анимировать между разными наборами модификаторов.
Следуя этому параграфу документации, вы можете анимировать значение для Modifier.size
.
Сначала я жду определения размера изображения, с этим значением size
можно установить модификатор ( then
до этого я использовал Modifier
с пустым значением), а затем это значение можно анимировать.
Вот пример:
Column {
val animatableSize = remember { Animatable(Size.Zero, Size.VectorConverter) }
val (containerSize, setContainerSize) = remember { mutableStateOf<Size?>(null) }
val (imageSize, setImageSize) = remember { mutableStateOf<Size?>(null) }
val density = LocalDensity.current
val scope = rememberCoroutineScope()
Button(onClick = {
scope.launch {
if (imageSize == null || containerSize == null) return@launch
val targetSize = if (animatableSize.value == imageSize) containerSize else imageSize
animatableSize.animateTo(
targetSize,
animationSpec = tween(durationMillis = 1000)
)
}
}) {
Text("Animate")
}
Box(
Modifier
.padding(20.dp)
.size(300.dp)
.background(Color.LightGray)
.onSizeChanged { size ->
setContainerSize(size.toSize())
}
) {
Image(
Icons.Default.Person,
contentDescription = null,
modifier = Modifier
.then(
if (animatableSize.value != Size.Zero) {
animatableSize.value.run {
Modifier.size(
width = with(density) { width.toDp() },
height = with(density) { height.toDp() },
)
}
} else {
Modifier
}
)
.onSizeChanged { intSize ->
if (imageSize != null) return@onSizeChanged
val size = intSize.toSize()
setImageSize(size)
scope.launch {
animatableSize.snapTo(size)
}
}
)
}
}
Результат:
Комментарии:
1. Спасибо за ответ. Не могли бы вы указать, какая зависимость требуется для класса DpSize? Я не могу его импортировать.
2. @ArpitShukla мой плохой.
DpSize
вводится в Compose1.1.0-beta
, см. Обновленный ответ без него.