Jetpack Compose动画 – 我可以在MotionLayout内使用AnimatedVisibility吗?

huangapple go评论106阅读模式
英文:

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(&quot;title&quot;),
) {
Text(
text = &quot;Foodie&quot;,
style = if (progress &gt; 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(&quot;logo&quot;),
) {
Image(
painter = painterResource(R.drawable.foodie_logo),
contentDescription = &quot;Foodie logo&quot;,
)
}
}
}

With splash_app_bar_motion_scene:

{
ConstraintSets: {
start: {
logo:{
width: 200,
height: 200,
start: [&#39;parent&#39;, &#39;start&#39;],
end: [&#39;parent&#39;, &#39;end&#39;],
top: [&#39;parent&#39;, &#39;top&#39;],
bottom: [&#39;parent&#39;, &#39;bottom&#39;]
},
title: {
start: [&#39;logo&#39;, &#39;start&#39;],
end: [&#39;logo&#39;, &#39;end&#39;],
top: [&#39;logo&#39;, &#39;bottom&#39;]
}
},
end: {
logo:{
width: 24,
height: 24,
start: [&#39;parent&#39;, &#39;start&#39;, 16],
top: [&#39;parent&#39;, &#39;top&#39;, 12],
bottom: [&#39;parent&#39;, &#39;bottom&#39;, 12],
},
title: {
start: [&#39;logo&#39;, &#39;end&#39;, 16],
top: [&#39;logo&#39;, &#39;top&#39;],
bottom: [&#39;logo&#39;, &#39;bottom&#39;]
}
}
},
Transitions: {
default: {
from: &#39;start&#39;,
to: &#39;end&#39;,
pathMotionArc: &#39;startVertical&#39;,
KeyFrames: {
KeyAttributes: [
{
target: [&#39;logo&#39;],
frames: [0, 100],
rotationZ: [0,  360]
},
{
target: [&#39;title&#39;],
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&lt;Data&gt;,
) {
Column {
SplashAppBar(
collapsed = uiState.initialized,
hideEnterAnimation = false,
)
when {
!uiState.initialized -&gt; Unit
uiState.errorMessage.isNotEmpty() -&gt; ErrorRetryView()
uiState.successData != null -&gt; SuccessView(uiState.successData)
uiState.loading -&gt; LoadingView()
else -&gt; 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 = &quot;motion101&quot;)
@Composable
fun M3MultiState() {
    val titleId = &quot;title&quot;


    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,&quot;loading&quot;) {
        }
        transition(  b,c,&quot;normal&quot;) {
        }
    }
    val painter = painterResource(id = R.drawable.pepper)

    var transitionName by remember {
        mutableStateOf(&quot;loading&quot;)
    }
    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 = &quot;normal&quot;
        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.

huangapple
  • 本文由 发表于 2023年4月4日 17:23:20
  • 转载请务必保留本文链接:https://go.coder-hub.com/75927672.html
匿名

发表评论

匿名网友

:?: :razz: :sad: :evil: :!: :smile: :oops: :grin: :eek: :shock: :???: :cool: :lol: :mad: :twisted: :roll: :wink: :idea: :arrow: :neutral: :cry: :mrgreen:

确定