animate*AsState関数は制御する値のタイプにより、数種類が準備されています。
どの関数も、animateValueAsStateが原型のラッパー関数であり、動作は同じです。
ここでは、各々の関数について、サンプルを示します。
また、animateValueAsStateのサンプルは、独自の関数を作る方法を紹介しています。
※環境:Android Studio Iguana | 2023.2.1
Kotlin 1.9.0
Compose Compiler 1.5.1
androidx.compose.animation:* 1.5.0
目次
関数の種類
animate*AsState関数は制御する値のタイプにより、次のような種類が準備されています。
| アニメーション関数 | タイプ | コメント |
|---|---|---|
| animateFloatAsState | Float | |
| animateIntAsState | Int | |
| animateDpAsState | Dp | |
| animateOffsetAsState | Offset | x、yはFloat型 x、yは独立して制御可能 |
| animateIntOffsetAsState | IntOffset | x、yはInt型 x、yは独立して制御可能 |
| animateSizeAsState | Size | width、heightはFloat型 width、heightは独立して制御可能 |
| animateIntSizeAsState | IntSize | width、heightはInt型 width、heightは独立して制御可能 |
| animateColorAsState | Color | a、r、g、bは独立して制御可能 |
| animateRectAsState | Rect | left、top、right、bottomはFloat型 left、top、right、bottomは独立して制御可能 |
| animateValueAsState | Value | 上記9つの原型になる関数 |
この中で、animateValueAsStateは原型になる関数です。他の関数はanimateValueAsStateのラッパー関数になります。
例:animateFloat/IntAsState
private val StartAlphaF = 0.0f
private val EndAlphaF = 1.0f
@Preview_mdpi
@Composable
private fun AnimateFloatAsStateSample() {
Box(
modifier = Modifier
.size(width = 320.dp, height = 240.dp)
.background(color = Color(0xFFF0F0FF)),
contentAlignment = Alignment.Center
) {
val _toggle = remember { mutableStateOf(false) }
val _alpha = animateFloatAsState(
targetValue = if(_toggle.value) EndAlphaF else StartAlphaF,
animationSpec = tween(durationMillis = 1000)
)
Image(
painter = painterResource(R.drawable.leaf1),
contentDescription = null,
modifier = Modifier
.background(color = Color.LightGray)
.alpha(_alpha.value)
.clickable { _toggle.value = !_toggle.value }
)
}
}
private val StartAlphaI = 0x00
private val EndAlphaI = 0xff
@Preview_mdpi
@Composable
private fun AnimateIntAsStateSample() {
Box(
modifier = Modifier
.size(width = 320.dp, height = 240.dp)
.background(color = Color(0xFFF0F0FF)),
contentAlignment = Alignment.Center
) {
val _toggle = remember { mutableStateOf(false) }
val _alpha = animateIntAsState(
targetValue = if(_toggle.value) EndAlphaI else StartAlphaI,
animationSpec = tween(durationMillis = 1000)
)
Image(
painter = painterResource(R.drawable.leaf1),
contentDescription = null,
modifier = Modifier
.background(color = Color.LightGray)
.alpha(_alpha.value.toFloat() / 255.0f)
.clickable { _toggle.value = !_toggle.value }
)
}
}
例:animateDpAsState
private val StartX = 20.dp
private val EndX = 260.dp
@Preview_mdpi
@Composable
private fun AnimateDpAsStateSample() {
Box(
modifier = Modifier
.size(width = 320.dp, height = 100.dp)
.background(color = Color(0xFFF0F0FF)),
contentAlignment = Alignment.CenterStart
) {
val _toggle = remember { mutableStateOf(false) }
val _posX = animateDpAsState(
targetValue = if(_toggle.value) EndX else StartX,
animationSpec = tween(durationMillis = 1000)
)
Image(
painter = painterResource(R.drawable.baseline_toys_black_36),
contentDescription = null,
modifier = Modifier
.size(36.dp)
.offset(x = _posX.value)
.clickable { _toggle.value = !_toggle.value }
)
}
}
※「関数の動作」で掲載したサンプルと同じ
例:animateOffset/IntOffsetAsState
private val StartOffset = Offset(20.0f, 20.0f) // Floatで表したDp値
private val EndOffset = Offset(260.0f, 180.0f) // Floatで表したDp値
@Preview_mdpi
@Composable
private fun AnimateOffsetAsStateSample() {
Box(
modifier = Modifier
.size(width = 320.dp, height = 240.dp)
.background(color = Color(0xFFF0F0FF))
) {
val _toggle = remember { mutableStateOf(false) }
val _offset = animateOffsetAsState(
targetValue = if(_toggle.value) EndOffset else StartOffset,
animationSpec = tween(durationMillis = 1000)
)
Image(
painter = painterResource(R.drawable.baseline_toys_black_36),
contentDescription = null,
modifier = Modifier
.size(36.dp) // ↓↓ offsetはDpを欲する ↓↓
.offset(x = _offset.value.x.dp, y = _offset.value.y.dp)
.clickable { _toggle.value = !_toggle.value }
)
}
}
private val StartIntOffset = IntOffset(20, 20) // Intで表したDp値
private val EndIntOffset = IntOffset(260, 180) // Intで表したDp値
@Preview_mdpi
@Composable
private fun AnimateIntOffsetAsStateSample() {
Box(
modifier = Modifier
.size(width = 320.dp, height = 240.dp)
.background(color = Color(0xFFF0F0FF))
) {
val _toggle = remember { mutableStateOf(false) }
val _offset = animateIntOffsetAsState(
targetValue = if(_toggle.value) EndIntOffset else StartIntOffset,
animationSpec = tween(durationMillis = 1000)
)
Image(
painter = painterResource(R.drawable.baseline_toys_black_36),
contentDescription = null,
modifier = Modifier
.size(36.dp) // ↓↓ offsetはPxを欲する ↓↓
.offset { _offset.value * density }
.clickable { _toggle.value = !_toggle.value }
)
}
}
例:animateSize/IntSizeAsState
private val StartSize = Size(50.0f, 210.0f) // Floatで表したDp値
private val EndSize = Size(200.0f, 150.0f) // Floatで表したDp値
@Preview_mdpi
@Composable
private fun AnimateSizeAsStateSample() {
Box(
modifier = Modifier
.size(width = 320.dp, height = 240.dp)
.background(color = Color(0xFFF0F0FF)),
contentAlignment = Alignment.Center
) {
val _toggle = remember { mutableStateOf(false) }
val _size = animateSizeAsState(
targetValue = if(_toggle.value) EndSize else StartSize,
animationSpec = tween(durationMillis = 1000)
)
Image(
painter = painterResource(R.drawable.leaf2),
contentDescription = null,
contentScale = ContentScale.FillBounds,
modifier = Modifier
.size(width = _size.value.width.dp, height = _size.value.height.dp)
.clickable { _toggle.value = !_toggle.value }
)
}
}
private val StartIntSize = IntSize(50, 210) // Intで表したDp値
private val EndIntSize = IntSize(200, 150) // Intで表したDp値
@Preview_mdpi
@Composable
private fun AnimateIntSizeAsStateSample() {
Box(
modifier = Modifier
.size(width = 320.dp, height = 240.dp)
.background(color = Color(0xFFF0F0FF)),
contentAlignment = Alignment.Center
) {
val _toggle = remember { mutableStateOf(false) }
val _size = animateIntSizeAsState(
targetValue = if(_toggle.value) EndIntSize else StartIntSize,
animationSpec = tween(durationMillis = 1000)
)
Image(
painter = painterResource(R.drawable.leaf2),
contentDescription = null,
contentScale = ContentScale.FillBounds,
modifier = Modifier
.size(width = _size.value.width.dp, height = _size.value.height.dp)
.clickable { _toggle.value = !_toggle.value }
)
}
}
例:animateColorAsState
private val StartColor = Color(0xffff0040)
private val EndColor = Color(0xff00ffc0)
@Preview_mdpi
@Composable
private fun AnimateColorAsStateSample() {
Box(
modifier = Modifier
.size(width = 320.dp, height = 240.dp)
.background(color = Color(0xFFF0F0FF)),
contentAlignment = Alignment.Center
) {
val _toggle = remember { mutableStateOf(false) }
val _color = animateColorAsState(
targetValue = if(_toggle.value) EndColor else StartColor,
animationSpec = tween(durationMillis = 1000)
)
Image(
painter = painterResource(R.drawable.leaf1),
contentDescription = null,
modifier = Modifier
.background(color = _color.value)
.scale(0.8f)
.clickable { _toggle.value = !_toggle.value }
)
}
}
例:animateRectAsState
private val StartRect = Rect( // Floatで表したDp値
Offset(x = 10.0f, y = 10.0f), Size(50.0f, 100.0f))
private val EndRect = Rect( // Floatで表したDp値
Offset(x = 170.0f, y = 150.0f), Size(100.0f, 50.0f))
@Preview_mdpi
@Composable
private fun AnimateRectAsStateSample() {
Box(
modifier = Modifier
.size(width = 320.dp, height = 240.dp)
.background(color = Color(0xFFF0F0FF)),
contentAlignment = Alignment.Center
) {
val _toggle = remember { mutableStateOf(false) }
val _rect = animateRectAsState(
targetValue = if(_toggle.value) EndRect else StartRect,
animationSpec = tween(durationMillis = 1000)
)
Canvas(
modifier = Modifier
.size(280.dp, 210.dp)
.background(color = Color.LightGray)
.clickable { _toggle.value = !_toggle.value }
) {
drawOval(
color = Color.Red,
topLeft = _rect.value.topLeft * density, // Dp->Px変換が必要
size = _rect.value.size * density) // Dp->Px変換が必要
}
}
}
例:animateValueAsState
animateValueAsStateは原型になる関数です。他の関数はanimateValueAsStateのラッパー関数になります。
他の関数と同様にVectorConverterを定義すれば、独自のanimateXXXAsState関数が作成できます。
以下は、3軸(X,Y,Z)の回転を行うRoataeXYZ.VectorConverterの例です。
data class RotateXYZ(val rX: Int, val rY: Int, val rZ: Int) { companion object }
private val RotateXYZToVector: TwoWayConverter<RotateXYZ, AnimationVector3D> =
TwoWayConverter(
convertToVector = {
AnimationVector3D(it.rX.toFloat(), it.rY.toFloat(), it.rZ.toFloat())
},
convertFromVector = {
RotateXYZ(it.v1.roundToInt(), it.v2.roundToInt(), it.v3.roundToInt())
}
)
val RotateXYZ.Companion.VectorConverter: TwoWayConverter<RotateXYZ, AnimationVector3D>
get() = RotateXYZToVector
制御する値の個数(1/2/3/4個)により、アニメエンジン(AnimationVector1D/2D/3D/4D)を切り替えて使用します。
※例はタイプの変換箇所を明確するために、あえてrX/rY/rZをInt型で作成しています。Floatにすれば、toFloatとroundToIntは不要です。
private val StartRotate = RotateXYZ(rX = 0, rY = 0, rZ = 0)
private val EndRotate = RotateXYZ(rX = 360, rY = 360, rZ = 360)
@Preview_mdpi
@Composable
private fun AnimateValueAsStateSample() {
Box(
modifier = Modifier
.size(width = 320.dp, height = 240.dp)
.background(color = Color(0xFFF0F0FF)),
contentAlignment = Alignment.Center
) {
val _toggle = remember { mutableStateOf(false) }
val _rotate = animateValueAsState(
targetValue = if(_toggle.value) EndRotate else StartRotate,
animationSpec = tween(durationMillis = 2000),
typeConverter = RotateXYZ.VectorConverter
)
Image(
painter = painterResource(R.drawable.bike),
contentDescription = null,
modifier = Modifier
.scale(0.7f)
.clickable { _toggle.value = !_toggle.value }
.graphicsLayer {
rotationX = _rotate.value.rX.toFloat()
rotationY = _rotate.value.rY.toFloat()
rotationZ = _rotate.value.rZ.toFloat()
}
)
}
}
関連記事:
