如您所提到的,这个动画应该由导航库实现,并且已经有相关问题单。
考虑到这一点,我会在此留下我的答案,希望对您有所帮助……
我来分成三个部分:
@Composable
fun SlideInAnimationScreen() {
// I'm using the same duration for all animations.
val animationTime = 300
// This state is controlling if the second screen is being displayed or not
var showScreen2 by remember { mutableStateOf(false) }
// This is just to give that dark effect when the first screen is closed...
val color = animateColorAsState(
targetValue = if (showScreen2) Color.DarkGray else Color.Red,
animationSpec = tween(
durationMillis = animationTime,
easing = LinearEasing
)
)
Box(Modifier.fillMaxSize()) {
// Both Screen1 and Screen2 are declared here...
}
}
// Screen 1
AnimatedVisibility(
!showScreen2,
modifier = Modifier.fillMaxSize(),
enter = slideInHorizontally(
initialOffsetX = { -300 }, // small slide 300px
animationSpec = tween(
durationMillis = animationTime,
easing = LinearEasing // interpolator
)
),
exit = slideOutHorizontally(
targetOffsetX = { -300 }, =
animationSpec = tween(
durationMillis = animationTime,
easing = LinearEasing
)
)
) {
Box(
Modifier
.fillMaxSize()
.background(color.value) // animating the color
) {
Button(modifier = Modifier.align(Alignment.Center),
onClick = {
showScreen2 = true
}) {
Text(text = "Ok")
}
}
}
// Screen 2
AnimatedVisibility(
showScreen2,
modifier = Modifier.fillMaxSize(),
enter = slideInHorizontally(
initialOffsetX = { it }, // it == fullWidth
animationSpec = tween(
durationMillis = animationTime,
easing = LinearEasing
)
),
exit = slideOutHorizontally(
targetOffsetX = { it },
animationSpec = tween(
durationMillis = animationTime,
easing = LinearEasing
)
)
) {
Box(
modifier = Modifier
.fillMaxSize()
.background(Color.Blue)
) {
Button(modifier = Modifier.align(Alignment.Center),
onClick = {
showScreen2 = false
}) {
Text(text = "Back")
}
}
}
这是结果:
在研究了CrossFade的代码后,我为交叉幻灯片实现了类似的功能,并且它还支持在按下后退按钮时进行反向动画。
这里是代码:https://gist.github.com/DavidIbrahim/5f4c0387b571f657f4de976822c2a225
使用示例
@Composable
fun CrossSlideExample(){
var currentPage by remember { mutableStateOf("A") }
CrossSlide(targetState = currentPage, reverseAnimation: Boolean = false) { screen ->
when (screen) {
"A" -> Text("Page A")
"B" -> Text("Page B")
}
}
}
https://github.com/google/accompanist/tree/main/navigation-animation
@Composable
fun ViewSliderDemo() {
val slider = ViewSlider()
Box(Modifier.fillMaxSize()) {
slider.View(
type= ViewSlider.Type.parent,
) {
Button(modifier = Modifier.align(Alignment.Center),
onClick = {
slider.state.value = true
}) {
Text(text = "Child >>>>")
}
}
slider.View(
type= ViewSlider.Type.child,
) {
Button(modifier = Modifier.align(Alignment.Center),
onClick = {
slider.state.value = false
}) {
Text(text = "<<<< Parent")
}
}
}
}
代码部分 — — —
class Observed<T>(startWith: T) : MutableState<T> {
private var _value by mutableStateOf(startWith)
override var value: T = startWith
get() = _value
set(value) {
_value = value
field = value
}
override fun component1(): T = value
override fun component2(): (T) -> Unit = { value = it }
}
class ViewSlider {
val animationTime = 300
var state = Observed(startWith = false)
enum class Type { parent, child }
@OptIn(ExperimentalAnimationApi::class)
@Composable
fun View(
type: Type,
content: @Composable () -> Unit
) {
var shadowSize by remember { mutableStateOf( 10.dp ) }
AnimatedVisibility(
visible = if (type == Type.parent) !state.value else state.value,
modifier = Modifier.fillMaxSize(),
enter = slideInHorizontally(
initialOffsetX = {
if (type == Type.parent) -300 else it
},
animationSpec = tween(
durationMillis = animationTime,
easing = LinearEasing // interpolator
)
),
exit = slideOutHorizontally(
targetOffsetX = {
if (type == Type.parent) -300 else it
},
animationSpec = tween(
durationMillis = animationTime,
easing = LinearEasing
)
)
) {
shadowSize = if (this.transition.currentState == this.transition.targetState) 0.dp else 10.dp
Box {
if (type == Type.child) {
Row {
Box(Modifier
.fillMaxHeight()
.width(shadowSize)
.background(
brush = Brush.horizontalGradient(
colors = listOf(Color.LightGray, Color.Gray)
)
)
)
Spacer(Modifier.weight(1f))
}
}
content()
if (type == Type.parent) {
Row {
Spacer(Modifier.weight(1f))
Box(Modifier
.fillMaxHeight()
.width(shadowSize)
.background(
brush = Brush.horizontalGradient(
colors = listOf(Color.Gray, Color.LightGray)
)
)
)
}
}
}
}
}
}
目前,Compose 中没有与 Activity
转换相媲美的东西。
我希望 Jetpack 正在开发它们。许多转换 API 要么是 internal
,要么是 private
,因此实现一个好的转换更加困难。
如果用于生产,请使用带有 Navigation Host 的 Activity/Fragment
。如果不使用导航组件,则可以使用 AnimatedVisibility
进行滑动。
enter
和exit
动画。 - kaustubhpatange