1. Introduction

Jetpack Compose is a new Android toolkit for building native interfaces launched by Google, which simplifies and speeds up interface development on Android. Jetpack Compose is a declarative UI framework. With the introduction of this framework, it marks the beginning of Android's full embrace of declarative UI development. Jetpack Compose has many advantages: the code is more concise and intuitive, application development efficiency is significantly improved, Kotlin API functions are intuitive, and the preview tool is powerful.

2. Development environment

In order to get a better development experience, the author here uses the Android Studio Canary version, so there is no need to configure some settings and dependencies. ( download address )

Open the project and create a new Empty Compose activity template. You need to pay attention to the build.gradle in the root directory. The related dependencies com.android.tools.build and org.jetbrains.kotlin versions need to correspond, otherwise there may be an error. Use it here Is:

dependencies {
    classpath "com.android.tools.build:gradle:7.0.0-alpha15"
    classpath "org.jetbrains.kotlin:kotlin-gradle-plugin:1.4.30"
}

This completes the new project.

Three, Jetpack Compose animation

Jetpack Compose provides some powerful and extensible APIs that can be used to easily implement various animation effects in the application interface. The following will introduce the common methods of Jetpack Compose Animations.

3.1 State Driven Animation: State

Jetpack Compose animation is to monitor the state, that is, to monitor the changes of the state value, so that the UI can be automatically updated. Composable functions can use remember or mutableStateOf to monitor changes in state values. If the state value is unchanged, the remember function will maintain the value in each recombination; if the state is mutable, it will trigger a reorganization when the value changes, and mutableStateOf will get a MutableState object, which is a Type of observation.

This reorganization is the key to creating state-driven animation. With reorganization, they are triggered when there is any change in the state of composable components. Compose animation is driven by State, and animation-related APIs are easier to use, making it easier to create beautiful declarative animations.

3.2 Visibility animation: AnimatedVisibility

First look at the function definition:

@ExperimentalAnimationApi
@Composable
fun AnimatedVisibility(
    visible: Boolean,
    modifier: Modifier = Modifier,
    enter: EnterTransition = fadeIn() + expandIn(),
    exit: ExitTransition = shrinkOut() + fadeOut(),
    initiallyVisible: Boolean = visible,
    content: @Composable () -> Unit
) {
    AnimatedVisibilityImpl(visible, modifier, enter, exit, initiallyVisible, content)
}

It can be seen that the default animation is fade in and zoom out, and fade out and contraction. In practice, various dynamic effects are realized by passing in different functions.

As the visible value changes, AnimatedVisibility can animate the appearance and disappearance of its content. The following code, you can control the appearance and disappearance of the picture by clicking the Button.

@Composable
fun AinmationDemo() {

    //AnimatedVisibility 可见动画
    var visible by remember { mutableStateOf(true) }

    Column(
        Modifier
            .fillMaxWidth()
            .fillMaxHeight(),
        Arrangement.Top,
        Alignment.CenterHorizontally
    ) {
        Button(
            onClick = { visible = !visible }
        ) {
            Text(text = if (visible) "Hide" else "Show")
        }

        Spacer(Modifier.height(16.dp))

        AnimatedVisibility(
            visible = visible,
            enter = slideInVertically() + fadeIn(),
            exit = slideOutVertically() + fadeOut()
        ) {
            Image(
                painter = painterResource(id = R.drawable.pikaqiu),
                contentDescription = null,
                Modifier.fillMaxSize()
            )
        }
    }
}

By monitoring the change of visible, the visibility animation of the picture can be realized, and the effect is shown in the small picture;

3.3 Layout size animation: AnimateContentSize

First look at the definition of the function:

fun Modifier.animateContentSize(
    animationSpec: FiniteAnimationSpec<IntSize> = spring(),
    finishedListener: ((initialValue: IntSize, targetValue: IntSize) -> Unit)? = null
)

You can set the animation speed and monitoring value for the layout size animation.

It can be seen from the definition of the function that this function is essentially an extension function of Modefier. You can monitor the state changes through the variable size to achieve the animation effect of the layout size, the code is as follows:

//放大缩小动画 animateContentSize
    var size by remember { mutableStateOf(Size(300F, 300F)) }

    Column(
        Modifier
            .fillMaxWidth()
            .fillMaxHeight(),
        Arrangement.Top,
        Alignment.CenterHorizontally
    ) {
        Spacer(Modifier.height(16.dp))

        Button(
            onClick = {
                size = if (size.height == 300F) {
                    Size(500F, 500F)
                } else {
                    Size(300F, 300F)
                }
            }
        ) {
            Text(if (size.height == 300F) "Shrink" else "Expand")
        }
        Spacer(Modifier.height(16.dp))

        Box(
            Modifier
                .animateContentSize()
        ) {
            Image(
                painter = painterResource(id = R.drawable.pikaqiu),
                contentDescription = null,
                Modifier
                    .animateContentSize()
                    .size(size = size.height.dp)
            )
        }
} //放大缩小动画 animateContentSize    var size by remember { mutableStateOf(Size(300F, 300F)) }​    Column(        Modifier            .fillMaxWidth()            .fillMaxHeight(),        Arrangement.Top,        Alignment.CenterHorizontally    ) {        Spacer(Modifier.height(16.dp))​        Button(            onClick = {                size = if (size.height == 300F) {                    Size(500F, 500F)                } else {                    Size(300F, 300F)                }            }        ) {            Text(if (size.height == 300F) "Shrink" else "Expand")        }        Spacer(Modifier.height(16.dp))​        Box(            Modifier                .animateContentSize()        ) {            Image(                painter = painterResource(id = R.drawable.pikaqiu),                contentDescription = null,                Modifier                    .animateContentSize()                    .size(size = size.height.dp)            )        }}

Through the click of the Button, monitor the change of the size value, and use animateContentSize() to achieve the animation effect. The specific animation effect is shown in the following figure:

3.4 Layout switching animation: Crossfade

Crossfade can add animation effects between the two layouts by monitoring the changes in state values and using fade-in and fade-out animations. The function itself is a Composable. The code is as follows:

//Crossfade 淡入淡出动画
    var fadeStatus by remember { mutableStateOf(true) }

    Column(
        Modifier
            .fillMaxWidth()
            .fillMaxHeight(),
        Arrangement.Top,
        Alignment.CenterHorizontally
    ) {
        Button(
            onClick = { fadeStatus = !fadeStatus }
        ) {
            Text(text = if (fadeStatus) "Fade In" else "Fade Out")
        }

        Spacer(Modifier.height(16.dp))

        Crossfade(targetState = fadeStatus, animationSpec = tween(3000)) { screen ->
            when (screen) {
                true -> Image(
                    painter = painterResource(id = R.drawable.pikaqiu),
                    contentDescription = null,
                    Modifier
                        .animateContentSize()
                        .size(300.dp)
                )
                false -> Image(
                    painter = painterResource(id = R.drawable.pikaqiu2),
                    contentDescription = null,
                    Modifier
                        .animateContentSize()
                        .size(300.dp)
                )
            }
        }

    }

Also by monitoring the value of fadeStatus, the animation of layout switching is realized. The specific dynamic effects are shown in the figure:

3.5 Single value animation: animate*AsState

Add animation effects to a single value. Just provide the end value (or target value), and the API will start from the current value and play the animation to the specified value.

Jetpack Compose provides many built-in functions to make animations for different types of data, such as: animateColorAsState, animateDpAsState, animateOffsetAsState, etc. Here we will introduce the use of animateFooAsState, the code is as follows:

//animate*AsState 单个值添加动画
    var transparent by remember { mutableStateOf(true) }
    val alpha: Float by animateFloatAsState(if (transparent) 1f else 0.5f)

    Column(
        Modifier
            .fillMaxWidth()
            .fillMaxHeight(),
        Arrangement.Top,
        Alignment.CenterHorizontally
    ) {
        Button(
            onClick = { transparent = !transparent }
        ) {
            Text(if (transparent) "Light" else "Dark")
        }

        Spacer(Modifier.height(16.dp))

        Box {

            Image(
                painter = painterResource(id = R.drawable.pikaqiu),
                contentDescription = null,
                Modifier
                    .animateContentSize()
                    .graphicsLayer(alpha = alpha)
                    .size(300.dp)
            )
        }
}

The animation effect is shown in the figure below:

3.6 Combined animation: updateTransition

Transition can track one or more animations at the same time and synchronize these animations between multiple states. The specific code is as follows:

var imagePosition by remember { mutableStateOf(ImagePosition.TopLeft) }

    Column(
        Modifier
            .fillMaxWidth()
            .fillMaxHeight(),
        Arrangement.Top,
        Alignment.CenterHorizontally
    ) {
        Spacer(Modifier.height(16.dp))

        val transition = updateTransition(targetState = imagePosition, label = "")
        val boxOffset by transition.animateOffset(label = "") { position ->
            when (position) {
                ImagePosition.TopLeft -> Offset(-60F, 0F)
                ImagePosition.BottomRight -> Offset(60F, 120F)
                ImagePosition.TopRight -> Offset(60F, 0F)
                ImagePosition.BottomLeft -> Offset(-60F, 120F)
            }
        }
        Button(onClick = {
            imagePosition = ChangePosition(imagePosition)
        }) {
            Text("Change position")
        }
        Box {

            Image(
                painter = painterResource(id = R.drawable.pikaqiu),
                contentDescription = null,
                Modifier
                    .offset(boxOffset.x.dp, boxOffset.y.dp)
                    .animateContentSize()
                    .size(300.dp)
            )
        }
}

Among them, ImagePosition and ChangePosition are defined enumeration classes and custom functions respectively.

enum class ImagePosition {
    TopRight,
    TopLeft,
    BottomRight,
    BottomLeft
}

fun ChangePosition(position: ImagePosition) =
    when (position) {
        ImagePosition.TopLeft -> ImagePosition.BottomRight
        ImagePosition.BottomRight -> ImagePosition.TopRight
        ImagePosition.TopRight -> ImagePosition.BottomLeft
        ImagePosition.BottomLeft -> ImagePosition.TopLeft
    }

The animation is shown in the following figure:

Fourth, the conclusion

Jetpack Compose has simplified animation to the point where you only need to create declarative code in our composable functions, just write the way you want the UI to animate, and Compose manages the rest. Finally, this is also the main goal of Jetpack Compose: to create a declarative UI toolkit to accelerate application development and improve code readability and logic.

The declarative UI toolkit provided by Jetpack Compose can achieve more functions with less code, and the readability and logic of the code are greatly improved.

Author: vivo Internet game client team-Ke Jie

vivo互联网技术
3.3k 声望10.2k 粉丝