Compose 动画 (五) : animateContentSize / animateEnterExit / Crossfade / AnimatedContent

1. 前言

AnimatedVisibilityanimateXxxAsStateanimateContentSizeCrossfadeAnimatedContent 这几个API,都是Compose的高级别动画API,是比较易用的。
上两篇文章我们已经介绍了AnimatedVisibility,这篇文章我们会介绍剩下的那几个API

2. animateContentSize

使用animateContentSize,可让Compose组件大小发生变化的时候,具备动画的效果。

比如,我们以下面这个例子为例

var message by remember {
    
     mutableStateOf("Hello") }
Column {
    
    
    Button(onClick = {
    
    
        message += "Hello"
    }) {
    
    
        Text(text = "Change")
    }
    Spacer(modifier = Modifier.height(16.dp))
    Box(
        modifier = Modifier
            .background(Color.Blue)
            .padding(10.dp)
            .animateContentSize(),
        contentAlignment = Alignment.Center
    ) {
    
    
        Text(text = message, color = Color.White)
    }
}

可以看到效果

在这里插入图片描述

当然,我们也可以给动画添加一些特效,比如回弹效果

var message by remember {
    
     mutableStateOf("Hello") }
Column {
    
    
    Button(onClick = {
    
    
        message += "Hello"
    }) {
    
    
        Text(text = "Change")
    }
    Spacer(modifier = Modifier.height(16.dp))
    Box(
        modifier = Modifier
            .background(Color.Blue)
            .padding(10.dp)
            .animateContentSize(spring(Spring.DampingRatioHighBouncy)),
        contentAlignment = Alignment.Center
    ) {
    
    
        Text(text = message, color = Color.White)
    }
}

在这里插入图片描述

关于回弹之类的animationSpec的动画效果,后续的文章会讲到,感兴趣的童鞋可以先关注一波。

注意:animateContentSize 在修饰符链中的位置顺序很重要。为了确保流畅的动画,请务必将其放置在任何大小修饰符(如 size 或 defaultMinSize)前面,以确保 animateContentSize 会将带动画效果的值的变化报告给布局。

3. animateEnterExit

animateEnterExit需要使用在AnimatedVisibility里面(直接或间接子项)。
我们可以使用Modifier.animateEnterExit为每个子项指定不同的动画效果。
比如这里添加的slide动画会覆盖AnimatedVisibility设置的fade动画。

var visible by remember {
    
    
        mutableStateOf(false)
    }
    AnimatedVisibility(
        visible = visible,
        enter = fadeIn(tween(durationMillis = 2000)),
        exit = fadeOut(tween(durationMillis = 2000))
    ) {
    
    
        Box(
            Modifier
                .fillMaxSize()
                .background(Color.Gray)
        ) {
    
    
            Box(
                Modifier
                    .align(Alignment.Center)
                    .animateEnterExit(
                        enter = slideInVertically(tween(durationMillis = 2000)),
                        exit = slideOutVertically(tween(durationMillis = 2000))
                    )
                    .sizeIn(minWidth = 64.dp, minHeight = 64.dp)
                    .background(Purple700)
            ) {
    
    
                //Content of the notification...
            }
        }
    }

    LaunchedEffect(key1 = Unit, block = {
    
    
        delay(2500)
        visible = true
    })

效果如下,这里动画的时长设为了2秒,可以看的更清楚一些

在这里插入图片描述
有时我们希望AnimatedVisibility内部每个子组件的过渡动画各不相同,
那么就要为AnimatedVisibilityenterexit参数分别设置EnterTransition.NoneExitTransition.None,然后就可以在每个组件分别指定animateEnterExit了。

animateEnterExit 显示是试验性质的,将来可能会发生变化,也可能会被完全移除。

4. Crossfade

Crossfade用来将页面上显示的内容进行切换。
两个交替出现的组件进行渐变的切换,一个消失,而另一个显示出来。
动画效果是淡入淡出,并对尺寸进行处理 (瞬间改变)。
Crossfade只支持这一种动画效果,是对于AnimatedContent (我们下面会讲到) 的简化版本。

4.1 Crossfade的使用

我们来试一下

Column(horizontalAlignment = Alignment.CenterHorizontally) {
    
    
    var showPicture by remember {
    
     mutableStateOf(false) }
    Crossfade(targetState = showPicture) {
    
    
        if (it) {
    
    
            Image(
                painter = painterResource(id = R.mipmap.photot1),
                modifier = Modifier.width(300.dp),
                contentDescription = null
            )
        } else {
    
    
            Box(
                Modifier
                    .height(300.dp * 9 / 16)
                    .width(300.dp)
                    .background(Color.Blue)
            )
        }
    }
    Spacer(modifier = Modifier.height(10.dp))
    Button(onClick = {
    
     showPicture = !showPicture }) {
    
    
        Text(text = "切换")
    }
}

显示效果

在这里插入图片描述

4.2 Crossfade尺寸不一致的情况

如果切换的两个组件,尺寸不一致,就会出现如下情况

如果转场前是大尺寸,转场后是小尺寸,会等转场快结束的时候,变成小尺寸
如果转场前是小尺寸,转场后是大尺寸,会在转场刚开始的时候,变成大尺寸

Column(horizontalAlignment = Alignment.CenterHorizontally) {
    
    
    var showPicture by remember {
    
     mutableStateOf(false) }
    Crossfade(targetState = showPicture) {
    
    
        if (it) {
    
    
            Image(
                painter = painterResource(id = R.mipmap.photot1),
                modifier = Modifier.width(300.dp),
                contentDescription = null
            )
        } else {
    
    
            Box(
                Modifier
                    .size(60.dp)
                    .clip(RoundedCornerShape(30.dp))
                    .background(Color.Blue)
            )
        }
    }
    Spacer(modifier = Modifier.height(10.dp))
    Button(onClick = {
    
     showPicture = !showPicture }) {
    
    
        Text(text = "切换")
    }
}

效果如下所示

在这里插入图片描述

5. AnimatedContent

AnimatedContent用来控制多个组件的入场和出场,同时还能对入场和出场效果做定制
相当于是AnimatedVisibilityCrossfade的结合,AnimatedContent出入场动画效果的尺寸是渐变的,这个是区别于Crossfade的一个点。

AnimatedContent 显示是试验性质的,将来可能会发生变化,也可能会被完全移除。

Column(horizontalAlignment = Alignment.CenterHorizontally) {
    
    
    var showPicture by remember {
    
     mutableStateOf(false) }
    AnimatedContent(showPicture) {
    
    
        if (it) {
    
    
            Image(
                painter = painterResource(id = R.mipmap.photot1),
                modifier = Modifier.width(300.dp),
                contentDescription = null
            )
        } else {
    
    
            Box(
                Modifier
                    .size(60.dp)
                    .clip(RoundedCornerShape(30.dp))
                    .background(Color.Blue)
            )
        }
    }
    Spacer(modifier = Modifier.height(10.dp))
    Button(onClick = {
    
     showPicture = !showPicture }) {
    
    
        Text(text = "切换")
    }
}

显示效果如下所示

在这里插入图片描述

5.1 实现数字增加的效果

使用AnimatedContent,设置自定义的动画效果,来实现数字增加的效果

Column {
    
    
    var count by remember {
    
     mutableStateOf(0) }
    Button(onClick = {
    
     count++ }, Modifier.width(100.dp)) {
    
    
        Text("增加")
    }
    Button(onClick = {
    
     count-- }, Modifier.width(100.dp)) {
    
    
        Text("减少")
    }
    Box(
        Modifier
            .size(100.dp)
            .border(1.dp, Color.LightGray)
            .padding(5.dp)
            .background(Color(0xFFF5F5F5)), contentAlignment = Alignment.Center
    ) {
    
    
        AnimatedContent(targetState = count) {
    
     targetCount ->
            Text(text = "$targetCount", fontSize = 20.sp)
        }
    }
}

显示效果如下
在这里插入图片描述

我们给它设置一个自定义的动画效果

transitionSpec = {
    
    
    if (targetState > initialState) {
    
    
        slideInVertically {
    
     height -> height } + fadeIn() with
                slideOutVertically {
    
     height -> -height } + fadeOut()
    } else {
    
    
        slideInVertically {
    
     height -> -height } + fadeIn() with
                slideOutVertically {
    
     height -> height } + fadeOut()
    }.using(
        SizeTransform(clip = false)
    )
}

再次运行
在这里插入图片描述

6. Compose 动画系列

Compose 动画系列,后续持续更新
Compose 动画 (一) : animateXxxAsState 实现放大/缩小/渐变等效果
Compose 动画 (二) : 为什么animateDpAsState要用val ? MutableState和State有什么区别 ?
Compose 动画 (三) : AnimatedVisibility 从入门到深入
Compose 动画 (四) : AnimatedVisibility 各种入场和出场动画效果
Compose 动画 (五) : animateContentSize / animateEnterExit / Crossfade / AnimatedContent
Compose 动画 (六) : 使用Transition管理多个动画,实现动画预览
Compose 动画 (七) : 高可定制性的动画 Animatable

猜你喜欢

转载自blog.csdn.net/EthanCo/article/details/129383017