Анимировать видимость в композиции

У меня есть текст, который нужно анимировать, чтобы показать и скрыть значение null или нет. было бы прямолинейно, если бы обзорность отдельно регулировалась, но это то, что у меня получилось. В приведенном ниже коде анимация ввода работает, но анимация выхода не работает, поскольку текстовое значение равно нулю. Я могу что-то придумать с запоминанием старого значения, но не знаю, как это сделать.

      @Composable
fun ShowAnimatedText(
    text : String?
) {
    Column(
        modifier = Modifier.fillMaxWidth()
    ) {
        AnimatedVisibility(
            visible = text != null,
            enter = fadeIn(animationSpec = tween(2000)),
            exit = fadeOut(animationSpec = tween(2000))
        ) {
            text?.let {
                Text(text = it)
            }
        }
    }
}

3 ответа

Я думаю, что анимация затухания на самом деле работает «сама по себе».

подозреваю параметрtext: String?это значение, исходящее из поднятого "состояния" где-то наверхуShowAnimatedText, и поскольку вы непосредственно наблюдаете за ним внутри области анимации , когда вы меняете его на ноль, он мгновенно удаляетTextкомпонуемый, и вы не наблюдаете медленное затухание.

      AnimatedVisibility(
    ...
) {
    text?.let { // your'e directly observing a state over here
        Text(text = it)
    }
}

Это моя попытка завершить ваш фрагмент, основанный на моем предположении, и заставить его работать, постепенное появление работает, но желаемое постепенное исчезновение происходит мгновенно.

      @Composable
fun SomeScreen() {

    var text by remember {
        mutableStateOf<String?>("Initial Value")
    }

    Row(
        modifier = Modifier.fillMaxWidth()
    ) {
        Button(onClick = {
            text = "New Value"
        }) {
            Text("Set New Value")
        }

        Button(onClick = {
            text = null
        }) {
            Text("Remove Value")
        }

        AnimatedText(text = text)
    }
}

@Composable
fun ShowAnimatedText(
    text : String?
) {
    Column(
        modifier = Modifier.fillMaxWidth()
    ) {
        AnimatedVisibility(
            visible = text != null,
            enter = fadeIn(animationSpec = tween(2000)),
            exit = fadeOut(animationSpec = tween(2000))
        ) {
            text?.let {
                Text(text = it)
            }
        }
    }
}

Вы можете решить эту проблему, изменивtextна значение, не являющееся состоянием, и измените свою логику видимости с использования проверки допустимости значений NULL на некоторую «бизнес-логику», которая потребует, чтобы она былаvisibleилиhidden, изменив приведенные выше коды следующим образом.

      @Composable
fun SomeScreen() {

    var show by remember {
        mutableStateOf(true)
    }

    Row(
        modifier = Modifier.fillMaxWidth()
    ) {
        Button(onClick = {
            show = !show
        }) {
            Text("Set New Value")
        }

        AnimatedText(text = "Just A Value", show)
    }
}

@Composable
fun ShowAnimatedText(
    text : String?,
    show: Boolean
) {

    Column(
        modifier = Modifier.fillMaxWidth()
    ) {
        AnimatedVisibility(
            visible = show,
            enter = fadeIn(animationSpec = tween(2000)),
            exit = fadeOut(animationSpec = tween(2000))
        ) {

            text?.let {
                Text(text = it)
            }
        }
    }
}

Jetpack ComposeAnimatedContentAPI может быть тем, что вам нужно, поскольку вы получаете объект данных, передаваемый в лямбду, и можете проверить его на наличие значений NULL, например:

      @OptIn(ExperimentalAnimationApi::class)
@Composable
fun ShowAnimatedText(
    text : String?
) {
    Column(
        modifier = Modifier.fillMaxWidth()
    ) {
        AnimatedContent(
            targetState = text,
            transitionSpec = {
                fadeIn(animationSpec = tween(2000)) with
                fadeOut(animationSpec = tween(2000))
            }
        ) { text ->
            text?.let {
                Text(text = it)
            }
        }
    }
}

  • нет никаких сбоев анимации при сокрытии (например, когда значение становитсяnull)
  • это также учитывается родительским макетом (например, одноуровневые представления плавно анимируются, чтобы освободить место).

У меня была та же проблема, мне помог этот ответ: /questions/60840752/kak-ispolzovat-animatedvisibility-s-nulevyimi-znacheniyami/66034277#66034277

Я исправил это, запомнив предыдущее состояние (или не устанавливая нулевое значение), пока не завершится анимация выхода, если текст равен нулю.

Спасибо zab.y ваше предложение.

      @Composable
fun ShowAnimatedText(
    text : String?,
    show: Boolean
) {
    var localText by remember {
        mutableStateOf<String?>(null)
    }
    AnimatedContent(show, localText)
    LaunchedEffect(key1 = text, block = {
        if(text == null){
            delay(2000)
        }
        localText = text
    })
}
@Composable
private fun AnimatedContent(show: Boolean, localText: String?) {
    Column(
        modifier = Modifier.fillMaxWidth()
    ) {
        AnimatedVisibility(
            visible = show,
            enter = fadeIn(animationSpec = tween(2000)),
            exit = fadeOut(animationSpec = tween(2000))
        ) {
            localText?.let {
                Text(text = it)
            }
        }
    }
}