英文:
Jetpack compose animation - Can I use AnimatedVisibility inside MotionLayout?
问题
I am trying to display splash screen where elements would have enter animation and when data would be loaded for the first time then minimize it to top app bar and display data.
我正在尝试显示启动屏幕,其中元素将具有进入动画,当首次加载数据时,将其最小化到顶部应用栏并显示数据。
Edit:
I managed to put both animation to one screen, but I am not sure if this is okay to use AnimatedVisibility
inside MotionLayout
.
我设法将两个动画放在一个屏幕上,但我不确定是否可以在MotionLayout
内使用AnimatedVisibility
。
My SplashAppBar
looks this way:
我的SplashAppBar
如下所示:
fun SplashAppBar(
collapsed: Boolean,
hideEnterAnimation: Boolean = true,
) {
// ... (代码部分省略)
}
With splash_app_bar_motion_scene
:
使用splash_app_bar_motion_scene
:
{
ConstraintSets: {
start: {
logo:{
width: 200,
height: 200,
start: ['parent', 'start'],
end: ['parent', 'end'],
top: ['parent', 'top'],
bottom: ['parent', 'bottom']
},
title: {
start: ['logo', 'start'],
end: ['logo', 'end'],
top: ['logo', 'bottom']
}
},
end: {
logo:{
width: 24,
height: 24,
start: ['parent', 'start', 16],
top: ['parent', 'top', 12],
bottom: ['parent', 'bottom', 12],
},
title: {
start: ['logo', 'end', 16],
top: ['logo', 'top'],
bottom: ['logo', 'bottom']
}
}
},
Transitions: {
default: {
from: 'start',
to: 'end',
pathMotionArc: 'startVertical',
KeyFrames: {
KeyAttributes: [
{
target: ['logo'],
frames: [0, 100],
rotationZ: [0, 360]
},
{
target: ['title'],
frames: [0, 20, 50, 80, 100],
translationY: [0,-100, -45, -10, 0],
translationX: [0, 0, -80, -85, 0],
}
]
}
}
}
}
The way I present it is:
我呈现的方式是:
@Composable
fun MainView(
uiState: UiState<Data>,
) {
// ... (代码部分省略)
}
英文:
I am trying to display splash screen where elements would have enter animation and when data would be loaded for the first time then minimize it to top app bar and display data.
Edit:
I managed to put both animation to one screen, but I am not sure if this is okay to use AnimatedVisibility
inside MotionLayout
.
My SplashAppBar
looks this way:
fun SplashAppBar(
collapsed: Boolean,
hideEnterAnimation: Boolean = true,
) {
val context = LocalContext.current
val motionScene = remember {
context.resources.openRawResource(R.raw.splash_app_bar_motion_scene).readBytes()
.decodeToString()
}
val progress by animateFloatAsState(
targetValue = if (collapsed) 1f else 0f,
tween(2000)
)
val motionHeight by animateDpAsState(
targetValue = if (collapsed) 56.dp else LocalConfiguration.current.screenHeightDp.dp,
tween(2000)
)
val fontSize by animateIntAsState(
targetValue = if (collapsed) 20 else 96,
tween(2000)
)
var visible by remember { mutableStateOf(hideEnterAnimation) }
val density = LocalDensity.current
LaunchedEffect(key1 = true) {
visible = true
}
MotionLayout(
motionScene = MotionScene(content = motionScene),
progress = progress,
modifier = Modifier
.fillMaxWidth()
.height(motionHeight)
.background(
Brush.verticalGradient(
listOf(
Color.Black,
BlueGray500
)
),
)
) {
AnimatedVisibility(
visible = visible,
enter = slideInVertically(
animationSpec = tween(
durationMillis = 1000,
easing = EaseOutCirc
)
) {
with(density) { 200.dp.roundToPx() }
} + fadeIn(initialAlpha = 0.3f),
modifier = Modifier.layoutId("title"),
) {
Text(
text = "Foodie",
style = if (progress > 0.5f) MaterialTheme.typography.h6 else MaterialTheme.typography.h1,
fontSize = fontSize.sp,
color = Color.White,
)
}
AnimatedVisibility(
visible = visible,
enter = slideInVertically(
animationSpec = tween(
durationMillis = 2000,
easing = EaseOutBounce
)
) {
with(density) { -500.dp.roundToPx() }
} + fadeIn(initialAlpha = 0.3f),
modifier = Modifier.layoutId("logo"),
) {
Image(
painter = painterResource(R.drawable.foodie_logo),
contentDescription = "Foodie logo",
)
}
}
}
With splash_app_bar_motion_scene
:
{
ConstraintSets: {
start: {
logo:{
width: 200,
height: 200,
start: ['parent', 'start'],
end: ['parent', 'end'],
top: ['parent', 'top'],
bottom: ['parent', 'bottom']
},
title: {
start: ['logo', 'start'],
end: ['logo', 'end'],
top: ['logo', 'bottom']
}
},
end: {
logo:{
width: 24,
height: 24,
start: ['parent', 'start', 16],
top: ['parent', 'top', 12],
bottom: ['parent', 'bottom', 12],
},
title: {
start: ['logo', 'end', 16],
top: ['logo', 'top'],
bottom: ['logo', 'bottom']
}
}
},
Transitions: {
default: {
from: 'start',
to: 'end',
pathMotionArc: 'startVertical',
KeyFrames: {
KeyAttributes: [
{
target: ['logo'],
frames: [0, 100],
rotationZ: [0, 360]
},
{
target: ['title'],
frames: [0, 20, 50, 80, 100],
translationY: [0,-100, -45, -10, 0],
translationX: [0, 0, -80, -85, 0],
}
]
}
}
}
}
The way I present it is:
@Composable
fun MainView(
uiState: UiState<Data>,
) {
Column {
SplashAppBar(
collapsed = uiState.initialized,
hideEnterAnimation = false,
)
when {
!uiState.initialized -> Unit
uiState.errorMessage.isNotEmpty() -> ErrorRetryView()
uiState.successData != null -> SuccessView(uiState.successData)
uiState.loading -> LoadingView()
else -> RetryView()
}
}
}
答案1
得分: 1
如果您已经在使用ConstraintLayout,请考虑使用MotionLayout。它允许非常大的自定义性。以下是一个编写飞入效果的示例。
@OptIn(ExperimentalMotionApi::class)
@Preview(group = "motion101")
@Composable
fun M3MultiState() {
val titleId = "title";
var scene = MotionScene() {
val titleRef = createRefFor(titleId)
val a = constraintSet {
constrain(titleRef) {
centerHorizontallyTo(parent, 0f)
centerVerticallyTo(parent, 0f)
}
}
val b = constraintSet {
constrain(titleRef) {
centerHorizontallyTo(parent, 1f)
centerVerticallyTo parent, 0f
}
}
val c = constraintSet {
constrain(titleRef) {
centerHorizontallyTo(parent, 1f)
centerVerticallyTo(parent, 1f)
}
}
transition(a, b, "loading") {
}
transition(b, c, "normal") {
}
}
val painter = painterResource(id = R.drawable.pepper)
var transitionName by remember {
mutableStateOf("loading")
}
var animateToEnd by remember { mutableStateOf(true) }
val progress = remember { Animatable(0f) }
LaunchedEffect(animateToEnd) {
val result = progress.animateTo(
if (animateToEnd) 1f else 0f,
animationSpec = tween(5000)
)
transitionName = "normal"
progress.snapTo(0f)
progress.animateTo(
if (animateToEnd) 1f else 0f,
animationSpec = tween(5000)
)
}
MotionLayout(
modifier = Modifier
.background(Color(0xFF221010))
.fillMaxSize()
.padding(1.dp),
motionScene = scene,
transitionName = transitionName,
progress = progress.value
) {
Text(
modifier = Modifier.layoutId(titleId),
text = transitionName,
fontSize = 30.sp,
color = Color.White
)
}
}
这是一个使用MotionLayout的三种状态的示例。关键是LaunchedEffect执行了两个过渡效果。您需要阻止第二个过渡,直到数据加载完成。
英文:
If you are already using ConstraintLayout consider MotionLayout.
It allows huge customizability.
Here is an example of coding a fly in effect.
This will be up soon on Compose MotionLayout Examples )
@OptIn(ExperimentalMotionApi::class)
@Preview(group = "motion101")
@Composable
fun M3MultiState() {
val titleId = "title"
var scene = MotionScene() {
val titleRef = createRefFor(titleId)
val a = constraintSet {
constrain(titleRef) {
centerHorizontallyTo(parent,0f)
centerVerticallyTo(parent,0f)
}
}
val b = constraintSet {
constrain(titleRef) {
centerHorizontallyTo(parent,1f)
centerVerticallyTo(parent,0f)
}
}
val c = constraintSet {
constrain(titleRef) {
centerHorizontallyTo(parent,1f)
centerVerticallyTo(parent,1f)
}
}
transition( a,b,"loading") {
}
transition( b,c,"normal") {
}
}
val painter = painterResource(id = R.drawable.pepper)
var transitionName by remember {
mutableStateOf("loading")
}
var animateToEnd by remember { mutableStateOf(true) }
val progress = remember { Animatable(0f) }
LaunchedEffect(animateToEnd) {
val result = progress.animateTo(
if (animateToEnd) 1f else 0f,
animationSpec = tween(5000)
)
transitionName = "normal"
progress.snapTo(0f)
progress.animateTo(
if (animateToEnd) 1f else 0f,
animationSpec = tween(5000)
)
}
MotionLayout(
modifier = Modifier
.background(Color(0xFF221010))
.fillMaxSize()
.padding(1.dp),
motionScene = scene,
transitionName = transitionName,
progress = progress.value
) {
Text(
modifier = Modifier.layoutId(titleId),
text = transitionName,
fontSize = 30.sp,
color = Color.White
)
}
}
Here is a 3 state example using MotionLayout.
The key here is the LaunchedEffect does both transitions.
You would need to block the second transition till data is loaded.
通过集体智慧和协作来改善编程学习和解决问题的方式。致力于成为全球开发者共同参与的知识库,让每个人都能够通过互相帮助和分享经验来进步。
评论