Compose_21--动画之AnimatedVisibility

动画是界面交互重要的要素之一,为界面交互增添了活性,给人视觉上的享受。下面我们来使用Android Compose中的动画。在使用Compsose中的动画之前,我们需要先引入动画依赖:

 // Animations 下面的version不唯一,本文中使用的是1.1.0
 implementation 'androidx.compose.animation:animation:$version'

AnimatedVisibility(实验性API,以后可能会变化或移除)

AnimatedVisibility可组合项可为内容的出现和消失添加动画效果,出现动画(EnterTransition)有fadeIn、slideIn、slideInHorizontally、slideInVertically、scaleIn、expandIn、expandHorizontally、expandVertically;与之对应的消失动画(ExitTransition)有fadeOut、slideOut、slideOutHorizontally、slideOutVertically、scaleOut、shrinkOut、shrinkHorizontally、shrinkVertically。

 一、基础动画

1.fadeIn和fadeOut

/**
 * fadeIn和fadeOut 
 */
@ExperimentalAnimationApi
@Composable
fun compose_21_fade(visible: Boolean) {
    AnimatedVisibility(
        visible = visible,
        enter = fadeIn(),
        exit = fadeOut()
    ) {
        Image(painter = painterResource(id = R.drawable.flower), contentDescription = "")
    }
}

2.slideIn和slideOut

@ExperimentalAnimationApi
@Composable
fun compose_21_slide(visible: Boolean) {
    val density = LocalDensity.current
    AnimatedVisibility(
        visible = visible,
//        enter = slideIn(
//            // 起始位置
//            initialOffset = { fullSize -> IntOffset(fullSize.width,fullSize.height) },
//        ),
//        exit = slideOut(
//            // 目标位置
//            targetOffset = { fullSize -> IntOffset(fullSize.width,fullSize.height) },
//        )

//        enter = slideInHorizontally(
//            // 起始X坐标位置
//            initialOffsetX = { fullWidth -> fullWidth }
//        ),
//        exit = slideOutHorizontally(
//            // 目标X坐标位置
//            targetOffsetX = { fullWidth -> fullWidth }
//        )
        enter = slideInVertically(
            // 起始X坐标位置
            initialOffsetY = { fullHeight -> fullHeight }
        ),
        exit = slideOutVertically(
            // 目标X坐标位置
            targetOffsetY = { fullHeight -> fullHeight }
        )
    ) {
        Image(painter = painterResource(id = R.drawable.flower), contentDescription = "")
    }
}

3.scaleIn和scaleOut

@ExperimentalAnimationApi
@Composable
fun compose_21_scale(visible: Boolean) {
    AnimatedVisibility(
        visible = visible,
        enter = scaleIn(),
        exit = scaleOut()
    ) {
        Image(painter = painterResource(id = R.drawable.flower), contentDescription = "")
    }
}

4.expand和shrink

@ExperimentalAnimationApi
@Composable
fun compose_21_expandShrink(visible: Boolean) {
    AnimatedVisibility(
        visible = visible,
//        enter = expandIn(expandFrom = Alignment.Center),
//        exit = shrinkOut(shrinkTowards = Alignment.Center)
//        enter = expandHorizontally (expandFrom = Alignment.Start),
//        exit = shrinkHorizontally(shrinkTowards = Alignment.Start)
        enter = expandVertically(expandFrom = Alignment.Top),
        exit = shrinkVertically(shrinkTowards = Alignment.Top)
    ) {
        Image(painter = painterResource(id = R.drawable.flower), contentDescription = "")
    }
}

5.使用上面写的方法

@ExperimentalAnimationApi
@Composable
fun animationControl() {
    var visible by remember { mutableStateOf(false) }
    Column {
        Button(
            onClick = { visible = !visible }, // 点击事
        ) {
            Text(text = "开始动画", color = Color.Black)

        }
        compose_21_expandShrink(visible)
    }
}

class Compose_21Activity : ComponentActivity() {

    @ExperimentalAnimationApi
    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)
        setContent {
            animationControl()
        }

    }
}

上面写的都是单个动画出现,但是如果我们要使用组合动画该怎么办呢?其实很简单,只要用+号把要组合的动画加在一起就行了,例如:

@ExperimentalAnimationApi
@Composable
fun compose_21_fadePlusScale(visible: Boolean) {
    AnimatedVisibility(
        visible = visible,
        enter = fadeIn() + scaleIn(),
        exit = fadeOut() + scaleOut()
    ) {
        Image(painter = painterResource(id = R.drawable.flower), contentDescription = "")
    }
}

看到这里,可能有一些小伙伴会在想如何设置动画的属性呢?同样很简单,只需要动画的animationSpec赋值就行,默认值是animationSpec=spring(...),除了spring之外还有tween、keyFrame、repeatable和infiniteRepeatable。本文在这里不做研究,只写可能用得比较多的例子:

@ExperimentalAnimationApi
@Composable
fun compose_21_fade(visible: Boolean) {
    AnimatedVisibility(
        visible = visible,
        enter = fadeIn(animationSpec = tween(  //帧动画
            durationMillis = 3000, // 持续时间(单位/ms)
            delayMillis = 1000,  // 延时时间(单位/ms)
            easing = FastOutSlowInEasing)),
        exit = fadeOut(animationSpec = repeatable(  // 重复动画
            iterations = 3,  // 重复次数必须大于0
            // 动画
            animation = tween(durationMillis = 3000, easing = FastOutSlowInEasing),
            repeatMode = RepeatMode.Restart  // 模式
        ))
    ) {
        Image(painter = painterResource(id = R.drawable.flower), contentDescription = "")
    }
}

二、监听动画状态

既然是动画,那么就有动画状态。对动画状态的监听是开发过程中比不可少的环节,我们可用通过AnimatedVisibility的MutableTransitionState来监听动画的状态。MutableTransactionState有两个重要的属性currentState和isIdle,我们可用通过这两个组合属性来判断AnimatedVisibility是否可见、出现中还是消失中。

// state是MutableTransactionState对象
when {
       state.isIdle && state.currentState -> "可见"
       !state.isIdle && state.currentState -> "消失中"
       state.isIdle && !state.currentState -> "不可见"
       else -> "出现中"
}

同样,写一个简单的例子:

@Composable
fun compose_21_animationState(state: MutableTransitionState) {
//    这里改为参数获取
//    val state = remember {
//        MutableTransitionState(false).apply {
//            // Start the animation immediately.
//            targetState = true
//        }
//    }
    Column {
        AnimatedVisibility(
            visibleState = state,
            enter = slideInHorizontally(animationSpec = tween(durationMillis = 2000, delayMillis = 1000)),
            exit = slideOutHorizontally(animationSpec = tween(durationMillis = 2000, delayMillis = 1000))

        ) {
            Image(painter = painterResource(id = R.drawable.flower), contentDescription = "")
        }
        Text(
            text = when {
                state.isIdle && state.currentState -> "可见"
                !state.isIdle && state.currentState -> "消失中"
                state.isIdle && !state.currentState -> "不可见"
                else -> "出现中"
            }
        )
    }
}

@ExperimentalAnimationApi
@Composable
fun animationControl() {
//    var visible by remember { mutableStateOf(true) }

    val state = remember {
        MutableTransitionState(false).apply {
            // Start the animation immediately.
            targetState = false
        }
    }

    Column {
        Button(
            onClick = {
//                visible = !visible
                state.targetState = !state.targetState
            }, // 点击事件
        ) {
            Text(text = "开始动画", color = Color.Black)

        }
        compose_21_animationState(state)
    }
}

三、子项动画

如果想给AnimatedVisibility的子项(直接或间接)添加动画的话,可用通过animateEnterExit来实现,但是子项的视觉效果均由AnimatedVisibility可组合项中的动画与子项自己的动画构成,以下是个简单例子:

@ExperimentalAnimationApi
@Composable
fun compose_21_sonAnimation(visible: Boolean) {
    AnimatedVisibility(
        visible = visible,
        enter = slideInVertically(),
        exit = slideOutVertically(animationSpec = tween(
            delayMillis = 1000  // 为了见效果,延时一秒
        ))
    ) {
        Box(Modifier.fillMaxSize().background(Color.DarkGray)) {
            Box(
                Modifier
                    .align(Alignment.Center)
                    .animateEnterExit(
                        // Slide in/out the inner box.
                        enter = slideInHorizontally (animationSpec = tween(
                            delayMillis = 1000  // 为了见效果,延时一秒
                        )),
                        exit = slideOutHorizontally()
                    )
                    .sizeIn(minWidth = 100.dp, minHeight = 100.dp)
                    .background(Color.Blue)
            ) {
                // Content of the notification…
            }
        }
    }
}

 注:如果你不应用AnimatedVisibility的动画的话,可以把AnimatedVisibility的enter设置为EnterAnimation.Noe;eixt设置为ExitAnimation.None。

四、自定义动画

可能对于某些需求来说,自带的动画不能满足,那么可以通过AnimatedVisibility的内容lambda内的transition属性访问底层的Transition实例(其所有动画状态都与AnimatedVisibility的进入和退出动画同时运行)。

 例如我想通过transition来改变某组合项的背景颜色和大小,可以这样子实现:

@ExperimentalAnimationApi
@Composable
fun compose_21_customizeAnimation(visible: Boolean){
    AnimatedVisibility(
        visible = visible,
        enter = fadeIn(animationSpec = tween(
            durationMillis = 3000,  // 为了见效果
            delayMillis = 1000
        )),
        exit = fadeOut(animationSpec = tween(
            durationMillis = 3000,  // 为了见效果
            delayMillis = 5000
        ))
    ) { // this: AnimatedVisibilityScope
        // Use AnimatedVisibilityScope#transition to add a custom animation
        // to the AnimatedVisibility.
        val background by transition.animateColor { state ->
            when(state) {
                EnterExitState.PreEnter -> Color.Red  // 进入前
                EnterExitState.Visible -> Color.Blue  // 可见
                EnterExitState.PostExit -> Color.Green  // 退出前
            }
        }

        val size by transition.animateDp { state ->
            when(state) {
                EnterExitState.PreEnter -> 50.dp
                EnterExitState.Visible -> 100.dp
                EnterExitState.PostExit -> 70.dp
            }
        }
        Box(modifier = Modifier
            .size(size)
            .background(background))
    }
}

你可能感兴趣的:(Android-compose,android)