Compose中动画的使用
@Composable
fun EasyAnimation() {
val visible = remember { mutableStateOf(false) }
Column(
modifier = Modifier
.fillMaxWidth()
.padding(10.dp)
) {
Button(
modifier = Modifier
.fillMaxWidth(),
onClick = { visible.value = !visible.value },
colors = ButtonDefaults.buttonColors(
//按钮背景颜色设置
containerColor = Blue,
//按钮字体颜色设置
contentColor = White
)
) {
Text("可见性动画")
}
AnimatedVisibility(
visible = visible.value,
//滑入动画
enter = slideIn { IntOffset(400, 400) } + expandIn(),
//滑出动画
exit = slideOut { IntOffset(400, 400) } + shrinkOut()
) {
Text(
text = "天青色等烟雨,而我在等你,炊烟袅袅升起,隔江千万里。",
modifier = Modifier.fillMaxWidth()
)
}
}
}
添加一个按钮,按钮下是一段内容,通过点击按钮显示或隐藏内容。
给显示隐藏的内容加动画动作需要用AnimatedVisibility控件,并在该控价下加入进入enter和退出exit动画的属性。
按钮的点击事件中加入visible.value = !visible.value判断按钮显示或隐藏。
@Composable
fun EasyAnimation2() {
val expend = remember { mutableStateOf(false) }
Column(
modifier = Modifier
.padding(start = 10.dp, end = 10.dp)
) {
Text(
text = "朋友圈一般指的是腾讯微信上的一个社交功能,于微信4.0版本2012年4月19日更新时上线 [1] ," +
"用户可以通过朋友圈发表文字和图片,同时可通过其他软件将文章或者音乐分享到朋友圈。" +
"用户可以对好友新发的照片进行“评论”或“赞”,其他用户只能看相同好友的评论或赞。",
fontSize = 16.sp,
//两端对齐
textAlign = TextAlign.Justify,
//末尾省略
overflow = TextOverflow.Ellipsis,
modifier = Modifier.animateContentSize(),
//最大行
maxLines = if (expend.value) Int.MAX_VALUE else 2
)
Text(
if (expend.value) "收起" else "全文",
color = Color.Blue,
modifier = Modifier
.clickable {
expend.value = !expend.value
}
)
}
}
布局大小动画类似于朋友圈的展开和折叠功能,在Column组件下定义一段文字和一个收起字体按钮用于点击实现内容的展开和折叠。
内容的最大行通过maxLines属性设置为两行,如果字体内容超过两行通过overflow属性在末尾显示省略号。
通过点击按钮和expend文字内容状态控制内容的展开和收起。
@Composable
fun AnimateAsStateTest() {
//使用remember记住一个State,表示Box的size状态
var isSmall by remember { mutableStateOf(true) }
//animateDpAsState动画相关配置
val size: Dp by animateDpAsState(if (isSmall) 40.dp else 100.dp, label = "") {
Log.e("aaa", "AnimateAsStateTest: $it")
}
Column(
modifier = Modifier.padding(start = 10.dp, end = 10.dp)
) {
Button(
onClick = { isSmall = !isSmall },
modifier = Modifier
.padding(vertical = 10.dp),
colors = ButtonDefaults.buttonColors(
//按钮背景颜色设置
containerColor = Blue,
//按钮字体颜色设置
contentColor = White
)
) {
Text("改变大小")
}
Box(
Modifier
.size(size)
.background(Red)
)
}
}
属性动画通过animateDpAsState控制组件的放大或缩小。
@Composable
fun InfiniteTransitionTest() {
//使用rememberInfiniteTransition创建infiniteTransition实例
val infiniteTransition = rememberInfiniteTransition(label = "")
//通过animateColor、animatedFloat或animatedValue添加子动画
val color by infiniteTransition.animateColor(
initialValue = Red,
targetValue = Green,
//通过infiniteRepeatable指定动画规范
animationSpec = infiniteRepeatable(
animation = tween(2000, easing = LinearEasing),
//重复模式:Reverse:执行完逆转执行 Restart:执行完重新开始执行
repeatMode = RepeatMode.Reverse
),
label = "",
)
Box(
Modifier
.padding(10.dp)
.size(360.dp)
.background(color)
)
val angle by infiniteTransition.animateFloat(
initialValue = 0.0f,
targetValue = 360.0f,
animationSpec = infiniteRepeatable(
animation = tween(3000, easing = LinearEasing),
repeatMode = RepeatMode.Restart
),
label = "",
)
Text(
modifier = Modifier
.padding(10.dp)
.rotate(angle),
text = "Hello World!"
)
}
使用rememberInfiniteTransition创建infiniteTransition实例,通过animateColor、animatedFloat或animatedValue添加子动画,通过infiniteRepeatable指定动画规范。
@Composable
fun ScrollTest() {
Box(
modifier = Modifier
.fillMaxSize()
.padding(10.dp),
) {
val offsetX = remember { mutableStateOf(0f) }
val offsetY = remember { mutableStateOf(0f) }
Box(
Modifier
.offset { IntOffset(offsetX.value.roundToInt(), offsetY.value.roundToInt()) }
.background(Blue)
.size(50.dp)
.pointerInput(Unit) {
detectDragGestures { change, dragAmount ->
change.consume()
offsetX.value += dragAmount.x
offsetY.value += dragAmount.y
}
}
)
}
}
拖动动画主要用的是pointerInput控件,通过改变x和y轴坐标,并利用change.consume()动态刷新组件来实现的。
完整代码:
import android.os.Bundle
import android.util.Log
import androidx.activity.ComponentActivity
import androidx.activity.compose.setContent
import androidx.compose.animation.AnimatedVisibility
import androidx.compose.animation.animateColor
import androidx.compose.animation.animateContentSize
import androidx.compose.animation.core.LinearEasing
import androidx.compose.animation.core.RepeatMode
import androidx.compose.animation.core.animateDpAsState
import androidx.compose.animation.core.animateFloat
import androidx.compose.animation.core.infiniteRepeatable
import androidx.compose.animation.core.rememberInfiniteTransition
import androidx.compose.animation.core.tween
import androidx.compose.animation.expandIn
import androidx.compose.animation.shrinkOut
import androidx.compose.animation.slideIn
import androidx.compose.animation.slideOut
import androidx.compose.foundation.background
import androidx.compose.foundation.clickable
import androidx.compose.foundation.gestures.detectDragGestures
import androidx.compose.foundation.layout.Box
import androidx.compose.foundation.layout.Column
import androidx.compose.foundation.layout.fillMaxSize
import androidx.compose.foundation.layout.fillMaxWidth
import androidx.compose.foundation.layout.offset
import androidx.compose.foundation.layout.padding
import androidx.compose.foundation.layout.size
import androidx.compose.foundation.layout.systemBarsPadding
import androidx.compose.foundation.rememberScrollState
import androidx.compose.foundation.verticalScroll
import androidx.compose.material3.Button
import androidx.compose.material3.ButtonDefaults
import androidx.compose.material3.MaterialTheme
import androidx.compose.material3.Surface
import androidx.compose.material3.Text
import androidx.compose.runtime.Composable
import androidx.compose.runtime.SideEffect
import androidx.compose.runtime.getValue
import androidx.compose.runtime.mutableStateOf
import androidx.compose.runtime.remember
import androidx.compose.runtime.setValue
import androidx.compose.ui.Modifier
import androidx.compose.ui.draw.rotate
import androidx.compose.ui.graphics.Color
import androidx.compose.ui.graphics.Color.Companion.Blue
import androidx.compose.ui.graphics.Color.Companion.Green
import androidx.compose.ui.graphics.Color.Companion.Red
import androidx.compose.ui.graphics.Color.Companion.White
import androidx.compose.ui.input.pointer.pointerInput
import androidx.compose.ui.text.style.TextAlign
import androidx.compose.ui.text.style.TextOverflow
import androidx.compose.ui.tooling.preview.Preview
import androidx.compose.ui.unit.Dp
import androidx.compose.ui.unit.IntOffset
import androidx.compose.ui.unit.dp
import androidx.compose.ui.unit.sp
import androidx.core.view.WindowCompat
import com.cwj.composedemo.ui.theme.ComposeDemoTheme
import com.google.accompanist.systemuicontroller.rememberSystemUiController
import kotlin.math.roundToInt
class MainActivity : ComponentActivity() {
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
WindowCompat.setDecorFitsSystemWindows(window, false)
setContent {
ComposeDemoTheme {
TransparentSystemBars(Color.Transparent, true)
Surface(
modifier = Modifier
.fillMaxSize()
//同时添加状态栏和导航栏高度对应的上下 padding
.systemBarsPadding(),
//只添加状态栏
// .statusBarsPadding()
//只添加导航啦
// .navigationBarsPadding()
color = MaterialTheme.colorScheme.background,
) {
Greeting()
}
}
}
}
}
/**
* 沉浸状态栏
* barsColor:状态栏和导航栏颜色
* useDarkIcons:状态栏字体颜色是否为暗色,默认为亮色
*/
@Composable
fun TransparentSystemBars(barsColor: Color, useDarkIcons: Boolean) {
val systemUiController = rememberSystemUiController()
SideEffect {
systemUiController.setSystemBarsColor(
//状态栏和导航栏颜色
color = barsColor,
//状态栏字体颜色是否为暗色,默认为亮色
darkIcons = useDarkIcons,
//导航栏对比是否强制,为false时导航栏透明,为true时半透明
isNavigationBarContrastEnforced = false,
)
}
}
@Composable
fun Greeting() {
//记住滚动位置
val scrollState = rememberScrollState()
Column(
modifier = Modifier
//设置纵向滚动
.verticalScroll(scrollState)
) {
EasyAnimation()
EasyAnimation2()
AnimateAsStateTest()
InfiniteTransitionTest()
ScrollTest()
}
}
/**
* 拖动事件
*/
@Composable
fun ScrollTest() {
Box(
modifier = Modifier
.fillMaxSize()
.padding(10.dp),
) {
val offsetX = remember { mutableStateOf(0f) }
val offsetY = remember { mutableStateOf(0f) }
Box(
Modifier
.offset { IntOffset(offsetX.value.roundToInt(), offsetY.value.roundToInt()) }
.background(Blue)
.size(50.dp)
.pointerInput(Unit) {
detectDragGestures { change, dragAmount ->
change.consume()
offsetX.value += dragAmount.x
offsetY.value += dragAmount.y
}
}
)
}
}
/**
* 多动画重复
*/
@Composable
fun InfiniteTransitionTest() {
//使用rememberInfiniteTransition创建infiniteTransition实例
val infiniteTransition = rememberInfiniteTransition(label = "")
//通过animateColor、animatedFloat或animatedValue添加子动画
val color by infiniteTransition.animateColor(
initialValue = Red,
targetValue = Green,
//通过infiniteRepeatable指定动画规范
animationSpec = infiniteRepeatable(
animation = tween(2000, easing = LinearEasing),
//重复模式:Reverse:执行完逆转执行 Restart:执行完重新开始执行
repeatMode = RepeatMode.Reverse
),
label = "",
)
Box(
Modifier
.padding(10.dp)
.size(360.dp)
.background(color)
)
val angle by infiniteTransition.animateFloat(
initialValue = 0.0f,
targetValue = 360.0f,
animationSpec = infiniteRepeatable(
animation = tween(3000, easing = LinearEasing),
repeatMode = RepeatMode.Restart
),
label = "",
)
Text(
modifier = Modifier
.padding(10.dp)
.rotate(angle),
text = "Hello World!"
)
}
/**
* 属性动画
*/
@Composable
fun AnimateAsStateTest() {
//使用remember记住一个State,表示Box的size状态
var isSmall by remember { mutableStateOf(true) }
//animateDpAsState动画相关配置
val size: Dp by animateDpAsState(if (isSmall) 40.dp else 100.dp, label = "") {
Log.e("aaa", "AnimateAsStateTest: $it")
}
Column(
modifier = Modifier.padding(start = 10.dp, end = 10.dp)
) {
Button(
onClick = { isSmall = !isSmall },
modifier = Modifier
.padding(vertical = 10.dp),
colors = ButtonDefaults.buttonColors(
//按钮背景颜色设置
containerColor = Blue,
//按钮字体颜色设置
contentColor = White
)
) {
Text("改变大小")
}
Box(
Modifier
.size(size)
.background(Red)
)
}
}
/**
* 布局大小动画
*/
@Composable
fun EasyAnimation2() {
val expend = remember { mutableStateOf(false) }
Column(
modifier = Modifier
.padding(start = 10.dp, end = 10.dp)
) {
Text(
text = "朋友圈一般指的是腾讯微信上的一个社交功能,于微信4.0版本2012年4月19日更新时上线 [1] ," +
"用户可以通过朋友圈发表文字和图片,同时可通过其他软件将文章或者音乐分享到朋友圈。" +
"用户可以对好友新发的照片进行“评论”或“赞”,其他用户只能看相同好友的评论或赞。",
fontSize = 16.sp,
//两端对齐
textAlign = TextAlign.Justify,
//末尾省略
overflow = TextOverflow.Ellipsis,
modifier = Modifier.animateContentSize(),
//最大行
maxLines = if (expend.value) Int.MAX_VALUE else 2
)
Text(
if (expend.value) "收起" else "全文",
color = Blue,
modifier = Modifier
.clickable {
expend.value = !expend.value
}
)
}
}
/**
* 可见性动画
*/
@Composable
fun EasyAnimation() {
val visible = remember { mutableStateOf(false) }
Column(
modifier = Modifier
.fillMaxWidth()
.padding(10.dp)
) {
Button(
modifier = Modifier
.fillMaxWidth(),
onClick = { visible.value = !visible.value },
colors = ButtonDefaults.buttonColors(
//按钮背景颜色设置
containerColor = Blue,
//按钮字体颜色设置
contentColor = White
)
) {
Text("可见性动画")
}
AnimatedVisibility(
visible = visible.value,
//滑入动画
enter = slideIn { IntOffset(400, 400) } + expandIn(),
//滑出动画
exit = slideOut { IntOffset(400, 400) } + shrinkOut()
) {
Text(
text = "天青色等烟雨,而我在等你,炊烟袅袅升起,隔江千万里。",
modifier = Modifier.fillMaxWidth()
)
}
}
}
@Preview(showBackground = true, showSystemUi = true)
@Composable
fun GreetingPreview() {
ComposeDemoTheme {
Greeting()
}
}
沉浸状态栏需要用到的三方库
implementation "com.google.accompanist:accompanist-systemuicontroller:0.31.0-alpha"
沉浸状态栏主要用到的代码
/**
* 沉浸状态栏
* barsColor:状态栏和导航栏颜色
* useDarkIcons:状态栏字体颜色是否为暗色,默认为亮色
*/
@Composable
fun TransparentSystemBars(barsColor: Color, useDarkIcons: Boolean) {
val systemUiController = rememberSystemUiController()
SideEffect {
systemUiController.setSystemBarsColor(
//状态栏和导航栏颜色
color = barsColor,
//状态栏字体颜色是否为暗色,默认为亮色
darkIcons = useDarkIcons,
//导航栏对比是否强制,为false时导航栏透明,为true时半透明
isNavigationBarContrastEnforced = false,
)
}
}
Column组件的modifier中添加verticalScroll属性可使内容在超出一屏时上下滑动。