https://kotlinlang.org logo
#compose
Title
# compose
l

loloof64

11/27/2020, 4:34 PM
I have created this short Proof of Concept component in order to experiment Drag and Drop. But whenever the user moving, it is considered as a dragging stop, instead of just a pause. How to correct that ?
Copy code
@Preview
@Composable
fun DragNDropComponent() {
    Column(modifier = Modifier.size(300.dp).background(Color.Red)) {
        var x by remember { mutableStateOf(0f) }
        var y by remember { mutableStateOf(0f) }
        var animationActive by remember { mutableStateOf(false) }

        val animatedX = FloatPropKey()
        val animatedY = FloatPropKey()

        val positionAnimation = transitionDefinition<DndResetState> {
            state(DndResetState.Start) {
                this[animatedX] = x
                this[animatedY] = y
            }
            state(DndResetState.End) {
                this[animatedX] = 0f
                this[animatedY] = 0f
            }

            transition {
                animatedX using tween(durationMillis = 700, easing = FastOutSlowInEasing)
                animatedY using tween(durationMillis = 700, easing = FastOutSlowInEasing)

            }
        }

        val positionAnimationState = transition(
            definition = positionAnimation,
            initState = DndResetState.Start,
            toState = DndResetState.End,
            onStateChangeFinished = {
                animationActive = false
                x = 0f
                y = 0f
            }
        )

        val offsetX = with(DensityAmbient.current) {
            (
                    if (animationActive) positionAnimationState[animatedX]
                    else x
                    ).toDp()
        }
        val offsetY = with(DensityAmbient.current) {
            (
                    if (animationActive) positionAnimationState[animatedY]
                    else y
                    ).toDp()
        }


        Column(
            modifier = Modifier
                .offset(offsetX, offsetY)
                .size(10.dp)
                .background(Color.Blue)
                .dragGestureFilter(
                    dragObserver = object : DragObserver {
                        override fun onDrag(dragDistance: Offset): Offset {
                            x += dragDistance.x
                            y += dragDistance.y
                            return dragDistance
                        }

                        override fun onCancel() {
                            // animationActive = true
                        }

                        override fun onStart(downPosition: Offset) {
                            x = 0f
                            y = 0f
                        }

                        override fun onStop(velocity: Offset) {
                            // animationActive = true
                        }
                    }, startDragImmediately = true // better for experience on mobile/tablet
                )
        ) {}
    }
}
I haven't found anything useful in documentation of dragGestureFilter. According to the documentation, it is impossible as DragStop is emitted whenever the dragging velocity is null (no more move detected).
j

Joost Klitsie

11/27/2020, 5:16 PM
what is the effect you observe?
l

loloof64

11/27/2020, 5:19 PM
What happens is that the blue square moves as long as the finger moves. But all is cancelled whenever I stop moving, even if I have not released the finger.
j

Joost Klitsie

11/27/2020, 5:23 PM
With the code snippet which I posted under your earlier comment, I do not experience that behavior (either in the preview or on my tablet)
l

loloof64

11/27/2020, 5:25 PM
Thank you. Unfortunately I cannot see your snippet.
j

Joost Klitsie

11/27/2020, 5:30 PM
Copy code
@Preview
@Composable
fun DragAndDropComponent() {
    Column(modifier = Modifier.size(300.dp).background(Color.Red)) {
        val x = animatedFloat(0f)
        val y = animatedFloat(0f)
        Box(modifier = Modifier
            .offset(
                with(DensityAmbient.current) { x.value.toDp() },
                with(DensityAmbient.current) { y.value.toDp() })
            .size(10.dp)
            .background(Color.Blue)
            .dragGestureFilter(
                object : DragObserver {
                    override fun onDrag(dragDistance: Offset): Offset {
                        x.animateTo(x.targetValue + dragDistance.x)
                        y.animateTo(y.targetValue + dragDistance.y)
                        return super.onDrag(dragDistance)
                    }
                    override fun onStop(velocity: Offset) {
                        super.onStop(velocity)
                        x.animateTo(0f)
                        y.animateTo(0f)
                    }
                    override fun onCancel() {
                        super.onCancel()
                        x.animateTo(0f)
                        y.animateTo(0f)
                    }
                },
                startDragImmediately = true
            )
        )
    }
}
👍 1
here it is
also, I added on top of this some adjustments to have more spring-like behavior:
Copy code
@Preview
@Composable
fun DragAndDropComponent() {
    Column(modifier = Modifier.size(300.dp).background(Color.Red)) {
        val x = animatedFloat(150f)
        val y = animatedFloat(150f)
        val bouncySpring = SpringSpec(
            dampingRatio = DampingRatioMediumBouncy,
            stiffness = StiffnessLow,
            visibilityThreshold = DefaultDisplacementThreshold)
        val stiffSpring = SpringSpec(
            dampingRatio = DampingRatioLowBouncy,
            stiffness = StiffnessHigh,
            visibilityThreshold = DefaultDisplacementThreshold)
        Box(modifier = Modifier
            .offset(
                with(DensityAmbient.current) { x.value.toDp() },
                with(DensityAmbient.current) { y.value.toDp() })
            .size(10.dp)
            .background(Color.Blue)
            .dragGestureFilter(
                object : DragObserver {
                    override fun onDrag(dragDistance: Offset): Offset {
                        x.animateTo(
                            targetValue = x.targetValue + dragDistance.x,
                            anim = stiffSpring
                        )
                        y.animateTo(
                            targetValue = y.targetValue + dragDistance.y,
                            anim = stiffSpring
                        )
                        return super.onDrag(dragDistance)
                    }

                    override fun onStop(velocity: Offset) {
                        super.onStop(velocity)
                        reset()
                    }

                    override fun onCancel() {
                        super.onCancel()
                        reset()
                    }

                    private fun reset() {
                        x.animateTo(
                            targetValue = 150f,
                            anim = bouncySpring)
                        y.animateTo(
                            targetValue = 150f,
                            anim = bouncySpring)
                    }
                },
                startDragImmediately = true
            )
        )
    }
}
👍 1
now it behaves a bit more natural
🚀 1
👍 1
l

loloof64

11/27/2020, 5:31 PM
Thank you 😃 I'm gonna use it right now
🙂 1
2 Views