关于android:高效动画实现原理Jetpack-Compose-初探索

49次阅读

共计 6204 个字符,预计需要花费 16 分钟才能阅读完成。

一、简介

Jetpack Compose 是 Google 推出的用于构建原生界面的新 Android 工具包,它可简化并放慢 Android 上的界面开发。Jetpack Compose 是一个申明式的 UI 框架,随着该框架的推出,标记着 Android 开始全面拥抱申明式 UI 开发。Jetpack Compose 存在很多长处:代码更加简洁直观、利用开发效率显著晋升、Kotlin API 性能直观、预览工具弱小等。

二、开发环境

为了取得更好的开发体验,笔者这里应用的是 Android Studio Canary 版本,这样能够无需配置一些设置和依赖。(下载地址)

关上工程,新建 Empty Compose activity 模版,须要留神的是根目录下的 build.gradle,相干的依赖 com.android.tools.build 和 org.jetbrains.kotlin 版本须要对应,否则可能呈现出错的情景,这里应用的是:

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

这样就实现了我的项目的新建。

三、Jetpack Compose 动画

Jetpack Compose 提供了一些功能强大且可扩大的 API,可用于在利用界面中轻松实现各种动画成果。下文将会对 Jetpack Compose Animations 的罕用办法进行介绍。

3.1 状态驱动动画:State

Jetpack Compose 动画是通过对状态的监听,即监听状态值的变动,使 UI 能实现自动更新。可组合函数能够应用 remember 或者 mutableStateOf 监听状态值的变动。如果状态值是不变的,remember 函数会在每次重新组合中放弃该值;如果状态是可变的,它会在值发生变化的时候触发重组,mutableStateOf 将失去一个 MutableState 对象,它是一个可察看类型。

这种重组是创立状态驱动动画的要害。利用重组,它们会在可组合组件的状态产生任何变动时被触发。Compose 动画是由 State 驱动的,动画相干的 API 也较容易上手,能比拟容易发明出丑陋的申明式动画。

3.2 可见性动画: AnimatedVisibility

首先看下函数定义:

@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)
}

能够看出默认的动画是淡入放大、淡出膨胀,理论中通过传入不同函数实现各种动效。

随着可见值的变动,AnimatedVisibility 可为其内容的呈现和隐没设置动画。如下代码,能够通过点击 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())
        }
    }
}

通过监听 visible 的变动,可实现图片的可见性动画,成果如小图所示;

3.3 布局大小动画:AnimateContentSize

先看下函数的定义:

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

能够为布局大小动画设置动画速度和监听值。

由函数的定义能够看出这个函数实质上就 Modefier 的一个扩大函数。能够通过变量 size 监听状态变动实现布局大小的动画成果,代码如下:

// 放大放大动画 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)            )        }}

通过 Button 的点击,监听 size 值的变动,利用 animateContentSize()实现动画成果,具体动效如下图所示:

3.4 布局切换动画: Crossfade

Crossfade 能够通过监听状态值的变动,应用淡入淡出的动画在两个布局之间增加动画成果,函数本身就是一个 Composable,代码如下:

//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)
                )
            }
        }

    }

同样通过监听 fadeStatus 的值,实现布局切换的动画,具体的动效如图所示:

3.5 单个值动画:animate*AsState

为单个值增加动画成果。只需提供完结值(或目标值),该 API 就会从以后值开始向指定值播放动画。

Jetpack Compose 提供了很多内置函数,能够为不同类型的数据制作动画,例如:animateColorAsState、animateDpAsState、animateOffsetAsState 等,这里将介绍下 animateFooAsState 的应用,代码如下:

//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)
            )
        }
}

动画成果如下图所示:

3.6 组合动画:updateTransition

Transition 可同时追踪一个或多个动画,并在多个状态之间同步这些动画。具体的代码如下:

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)
            )
        }
}

其中,ImagePosition、ChangePosition 别离为定义的枚举类、自定义函数。

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
    }

动画的如下图所示:

四、结语

Jetpack Compose 已将动画简化到只需在咱们的可组合函数中创立申明性代码的水平,只需编写心愿 UI 动画的形式,其余部分由 Compose 治理。最初,这也是是 Jetpack Compose 的次要指标:创立一个申明式 UI 工具包来减速利用程序开发并进步代码可读性和逻辑性。

Jetpack Compose 提供的申明式 UI 工具包,能做到应用更少的代码实现更多的性能,且代码的可读性和逻辑性也大大提高了。

作者:vivo 互联网游戏客户端团队 -Ke Jie

正文完
 0