如果我想在滑动后调用操作,如何在 Compose 中实现滑动 [英] How to, in Compose, implement swipe if I want invoke action after swipe

查看:35
本文介绍了如果我想在滑动后调用操作,如何在 Compose 中实现滑动的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

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

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

这是示例:

val size = 记住 { mutableStateOf(Size.Zero) }val swipeableState = rememberSwipeableState(0)val 宽度 = if (size.value.width == 0f) 1f else size.value.width - 60.dp.value * 2盒子(修饰符 = 修饰符.fillMaxWidth().onSizeChanged { size.value = Size(it.width.toFloat(), it.height.toFloat()) }.swipeable(状态 = swipeableState,anchors = mapOf(0f to 0, width to 1),阈值 = { _, _ ->分数阈值(0.3f)},方向 = 方向.水平).background(颜色.LightGray)){盒子(修饰符.offset { IntOffset(swipeableState.offset.value.roundToInt(), 0) }.size(60.dp).background(颜色.深灰色))}

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

我的问题是如果我想在滑动后调用操作,如何实现滑动".

解决方案

您可以使用 .

I have a problem with compose. I try implement simple calendar. I already have displayed month, and enabled change months by arrows.

Now I want to add change month by swipe. I tried with .swipeable

This is sample:

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

But this not work perfectly. I need to hold outside calendar area and I don't have idea how change state of month (that respond for display calendar).

My question is "how to implement swipe if I want invoke action after swipe".

解决方案

You can observe any state with Side Effects. In case you need to wait for an action to complete, you can use if + DisposableEffect:

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

When the animation starts, I create a DisposableEffect and when it ends, onDispose is called, which means that the animation is finished.

In the case of your code, this will also be triggered at startup, because when you change the anchors after onSizeChanged, this also starts the animation. But you can check if the value has been changed, so it's not a big problem.

To solve your basic problem, you also need to have three states: left, middle and right.


A little off-topic, about that line:

val width = if (size.value.width == 0f) 1f else size.value.width - 60.dp.value * 2

  1. You repeat this calculation every time you recompose, you shouldn't do that. You can use LaunchedEffect with size.value.width as the key, because width only needs to be recalculated when that value changes.
  2. You are trying to get the pixel value from DP by multiplying by 2. This is wrong, you need to use Dencity.


So the final code can look like this:

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

Result:

p.s. In case you don't need to animate anything during the swipe, I've moved this logic into a separate modifier.

这篇关于如果我想在滑动后调用操作,如何在 Compose 中实现滑动的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆