我正在尝试显示启动画面,其中元素将进入动画以及首次加载数据的时间,然后将其最小化到顶部应用程序栏并显示数据。
这里我面临两个问题:
SplashScreen
和 SplashAppBar
- 后者的初始状态是第一个的结束状态。SplashScreen
动画完成,那么它会闪烁,因为它没有达到结束状态。这就是我取得的成就——它有点管用,但我认为代码不是很好: https://www.youtube.com/embed/HOvhsFU2tMI
我这样创建了
SplashScreen
:
@Composable
fun SplashScreen(
modifier: Modifier = Modifier,
) {
ConstraintLayout(
modifier = modifier
.fillMaxSize()
.background(
Brush.verticalGradient(
listOf(
Color.Black,
BlueGray500
)
)
),
) {
var visible by remember { mutableStateOf(false) }
val density = LocalDensity.current
LaunchedEffect(key1 = true) {
visible = true
}
val (image, text) = createRefs()
AnimatedVisibility(
visible = visible,
enter = slideInVertically(
animationSpec = tween(
durationMillis = 2000,
easing = EaseOutBounce
)
) {
with(density) { -500.dp.roundToPx() }
} + fadeIn(initialAlpha = 0.3f),
modifier = Modifier
.constrainAs(image) {
top.linkTo(parent.top)
bottom.linkTo(parent.bottom)
start.linkTo(parent.start)
end.linkTo(parent.end)
}
) {
Image(
painter = painterResource(R.drawable.foodie_logo),
contentDescription = "Foodie logo",
modifier = Modifier.size(200.dp)
)
}
AnimatedVisibility(
visible = visible,
enter = slideInVertically(
animationSpec = tween(
durationMillis = 1000,
easing = EaseOutCirc
)
) {
with(density) { 200.dp.roundToPx() }
} + fadeIn(initialAlpha = 0.3f),
modifier = Modifier
.constrainAs(text) {
top.linkTo(image.bottom)
start.linkTo(image.start)
end.linkTo(image.end)
}
) {
Text(
text = "Foodie",
style = MaterialTheme.typography.h1,
color = Color.White,
)
}
}
}
和
SplashAppBar
这样:
@OptIn(ExperimentalMotionApi::class)
@Composable
fun SplashAppBar() {
var collapsed by remember { mutableStateOf(false) }
LaunchedEffect(key1 = true) {
collapsed = 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)
)
MotionLayout(
motionScene = MotionScene(content = motionScene),
progress = progress,
modifier = Modifier
.fillMaxWidth()
.height(motionHeight)
) {
Box(
modifier = Modifier
.layoutId("background")
.fillMaxSize()
.background(
Brush.verticalGradient(
listOf(
Color.Black,
BlueGray500
)
)
),
)
Text(
text = "Foodie",
style = if (progress > 0.5f) MaterialTheme.typography.h6 else MaterialTheme.typography.h1,
color = Color.White,
modifier = Modifier.layoutId("title"),
)
Image(
painter = painterResource(R.drawable.foodie_logo),
contentDescription = "Foodie logo",
modifier = Modifier.layoutId("logo"),
)
}
}
与
splash_app_bar_motion_scene
:
{
ConstraintSets: {
start: {
logo:{
width: 200,
height: 200,
start: ['parent', 'start'],
end: ['parent', 'end'],
top: ['background', 'top'],
bottom: ['background', 'bottom']
},
background: {
width: 'spread',
height: 'spread',
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: ['background', 'start', 16],
top: ['background', 'top', 12],
bottom: ['background', 'bottom', 12],
},
background: {
width: 'spread',
height: 56,
start: ['parent', 'start'],
end: ['parent', 'end'],
top: ['parent', 'top'],
},
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],
scaleX: [1, 0.5, 0, 0.5, 1],
scaleY: [1, 0.5, 0, 0.5, 1]
}
]
}
}
}
}
我呈现它们的方式是:
@Composable
fun MainView(
uiState: UiState<Data>,
) {
Column {
if (uiState.initialized) SplashAppBar()
else SplashScreen()
when {
!uiState.initialized -> Unit
uiState.errorMessage.isNotEmpty() -> ErrorRetryView()
uiState.successData != null -> SuccessView(uiState.successData)
uiState.loading -> LoadingView()
else -> RetryView()
}
}
}