Создание анимации Jetpack. Могу ли я использовать AnimatedVisibility внутри MotionLayout?
Я пытаюсь отобразить экран-заставку, где элементы будут вводить анимацию, и когда данные будут загружены в первый раз, затем сверните их в верхнюю панель приложения и отобразите данные.
Редактировать:
Мне удалось поместить обе анимации на один экран, но я не уверен, можно ли это использовать.AnimatedVisibility
внутриMotionLayout
.
МойSplashAppBar
выглядит так:
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",
)
}
}
}
С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],
}
]
}
}
}
}
Я представляю это так:
@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 ответ
Если вы уже используете ConstraintLayout, рассмотрите возможность MotionLayout. Это обеспечивает огромные возможности настройки. Вот пример эффективного кодирования мухи. Скоро это будет в разделе «Примеры Compose 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 выполняет оба перехода. Вам нужно будет заблокировать второй переход до загрузки данных.