Как реализовать анимацию translate scale в Jetpack Compose?

#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 вводится в Compose 1.1.0-beta , см. Обновленный ответ без него.