【问题标题】:How to, in Compose, implement swipe if I want invoke action after swipe如果我想在滑动后调用操作,如何在 Compose 中实现滑动
【发布时间】:2021-11-05 03:10:18
【问题描述】:

我在撰写时遇到问题。我尝试实现简单的日历。我已经显示了月份,并启用了通过箭头更改月份。

现在我想通过滑动添加更改月份。 我试过.swipeable

这是示例:

val size = remember { mutableStateOf(Size.Zero) }
val swipeableState = rememberSwipeableState(0)
val width = if (size.value.width == 0f) 1f else size.value.width - 60.dp.value * 2
Box(
    modifier = Modifier
        .fillMaxWidth()
        .onSizeChanged { size.value = Size(it.width.toFloat(), it.height.toFloat()) }
        .swipeable(
            state = swipeableState,
            anchors = mapOf(0f to 0, width to 1),
            thresholds = { _, _ -> FractionalThreshold(0.3f) },
            orientation = Orientation.Horizontal
        )
        .background(Color.LightGray)
) {
    Box(
        Modifier
            .offset { IntOffset(swipeableState.offset.value.roundToInt(), 0) }
            .size(60.dp)
            .background(Color.DarkGray)
    )
}

但这并不完美。我需要在日历区域之外举行,我不知道如何更改月份状态(响应显示日历)。

我的问题是“如果我想在滑动后调用动作,如何实现滑动”。

【问题讨论】:

    标签: android kotlin android-jetpack-compose


    【解决方案1】:

    您可以使用Side Effects 观察任何状态。如果您需要等待某个操作完成,您可以使用if + DisposableEffect

    if (swipeableState.isAnimationRunning) {
        DisposableEffect(Unit) {
            onDispose {
                println("animatin finished")
            }
        }
    }
    

    动画开始时,我创建一个DisposableEffect,结束时,onDispose被调用,表示动画结束。

    对于您的代码,这也会在启动时触发,因为当您在onSizeChanged 之后更改锚点时,这也会启动动画。但是你可以检查一下这个值是不是变了,所以问题不大。

    要解决你的基本问题,你还需要有左、中、右三种状态。


    有点题外话,关于那条线:

    val width = if (size.value.width == 0f) 1f else size.value.width - 60.dp.value * 2
    
    1. 您每次重构时都重复此计算,您不应该这样做。您可以使用LaunchedEffectsize.value.width 作为键,因为width 只需要在该值更改时重新计算。
    2. 您试图通过乘以 2 从 DP 中获取像素值。这是错误的,您需要使用 Dencity。

    所以最终的代码可以是这样的:

    enum class SwipeDirection(val raw: Int) {
        Left(0),
        Initial(1),
        Right(2),
    }
    
    @Composable
    fun TestScreen() {
        var size by remember { mutableStateOf(Size.Zero) }
        val swipeableState = rememberSwipeableState(SwipeDirection.Initial)
        val density = LocalDensity.current
        val boxSize = 60.dp
        val width = remember(size) {
            if (size.width == 0f) {
                1f
            } else {
                size.width - with(density) { boxSize.toPx() }
            }
        }
        val scope = rememberCoroutineScope()
        if (swipeableState.isAnimationRunning) {
            DisposableEffect(Unit) {
                onDispose {
                    when (swipeableState.currentValue) {
                        SwipeDirection.Right -> {
                            println("swipe right")
                        }
                        SwipeDirection.Left -> {
                            println("swipe left")
                        }
                        else -> {
                            return@onDispose
                        }
                    }
                    scope.launch {
                        // in your real app if you don't have to display offset,
                        // snap without animation
                        // swipeableState.snapTo(SwipeDirection.Initial)
                        swipeableState.animateTo(SwipeDirection.Initial)
                    }
                }
            }
        }
        Box(
            modifier = Modifier
                .fillMaxWidth()
                .onSizeChanged { size = Size(it.width.toFloat(), it.height.toFloat()) }
                .clickable { }
                .swipeable(
                    state = swipeableState,
                    anchors = mapOf(
                        0f to SwipeDirection.Left,
                        width / 2 to SwipeDirection.Initial,
                        width to SwipeDirection.Right,
                    ),
                    thresholds = { _, _ -> FractionalThreshold(0.3f) },
                    orientation = Orientation.Horizontal
                )
                .background(Color.LightGray)
        ) {
            Box(
                Modifier
                    .offset { IntOffset(swipeableState.offset.value.roundToInt(), 0) }
                    .size(boxSize)
                    .background(Color.DarkGray)
            )
        }
    }
    

    结果:

    附言如果您在滑动过程中不需要为任何内容设置动画,我已将此逻辑移至separate modifier

    【讨论】:

      猜你喜欢
      • 2022-12-22
      • 2022-11-06
      • 1970-01-01
      • 2015-09-15
      • 2012-05-11
      • 2013-10-03
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多