如何在android compose中实现两个视图之间的幻灯片进出动画同步

pbpqsu0x  于 2023-08-01  发布在  Android
关注(0)|答案(5)|浏览(115)

我知道我可以使用AnimatedVisibility Composable函数并为可见性动画实现滑入动画,但我想实现的是当一个布局在进入动画中时,另一个在退出动画中,类似于下图。

NB:我知道我应该为不同的屏幕使用导航合成,目的地之间的动画仍在开发中,但我想在屏幕的一部分内容上实现这一点,类似于CrossFade Animation。


的数据

cl25kdpy

cl25kdpy1#

正如你提到的,这个动画应该由导航库实现,有a ticket opened to that
考虑到这一点,我在这里留下我的答案,希望它能帮助你。
我把它分成三部分:
1.容器:

@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...
    }
}

字符串
1.第一个屏幕只是做一个小幻灯片,以创建视差效果。我也改变了背景颜色从红色到黑暗只是为了给予这个重叠/隐藏/黑暗的效果。

// 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")
        }
    }
}


1.第二个是真正的从边缘滑动。

// 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")
        }
    }
}


结果如下:
x1c 0d1x的数据

e5njpo68

e5njpo682#

在深入研究了CrossFade的代码之后,我为十字幻灯片实现了一个类似的代码,当按下backButton时,它可以启用反向动画
这是: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")
        }
    }
}

字符串

0s0u357o

0s0u357o3#

现在我们有了一个官方的解决方案。最近Google Accompanist添加了一个库,为Jetpack Navigation Compose提供了Compose Animation支持。
https://github.com/google/accompanist/tree/main/navigation-animation

nuypyhwy

nuypyhwy4#

这是一个基于@nglauber解决方案的集成解决方案,它提供了视图滑动独立于视图背景的错觉。
x1c 0d1x的数据
演示部分-

@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)
                                    )
                                )
                        )
                    }
                }
            }

        }
    }

}

ny6fqffe

ny6fqffe5#

到目前为止,我们还没有任何东西可以与Compose中的Activity Transitions相媲美。Jetpack应该在他们身上工作,我希望。大量的转换API要么是internal,要么是private,所以实现一个好的API更难。
如果用于生产,请使用Activity/Fragment与导航主机。如果没有,使用AnimatedVisibility在没有导航组件的情况下滑动。
https://issuetracker.google.com/issues/172112072

相关问题