2 votes

Jetpack compose Dessin sur les formes

J'ai ce problème intéressant dans un projet, où l'utilisateur doit être en mesure de dessiner la même forme sur la forme définie, j'ai réalisé cela jusqu'à présent, mais je veux vérifier si il / elle a dessiné sur la forme correctement en UNE SEULE FOIS. Si le doigt va à l'extérieur du carré le dessin actuel devrait se réinitialiser et mettre un message de toast comme unsucssesfull sinon dit succesfull, Comment puis-je vérifier si le dessin est sur le carré ?

image

Le carré blanc est dessiné avec la méthode drawRect() et le dessin par-dessus est effectué par l'utilisateur lui-même, grâce à Drawpath(). Le code est donné ci-dessous

class DrawingActivity : ComponentActivity() {

    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)
        setContent {
            MyDrawing()
        }
    }
}
@Composable
fun MyDrawing() {
    val actionIdle = 0
    val actionDown = 1
    val actionMove = 2
    val actionUp = 3
    //Path, current touch position and touch states
    val path = remember { Path() }
    var motionEvent by remember { mutableStateOf(actionIdle) }
    var currentPosition by remember { mutableStateOf(Offset.Unspecified) }
    val canvasColor: Color by remember { mutableStateOf(Color.LightGray) }
    val drawModifier = Modifier
        .fillMaxWidth()
        .fillMaxHeight()
        .background(canvasColor)
        .clipToBounds()
        .pointerInput(Unit) {
            forEachGesture {
                awaitPointerEventScope {

                    val down: PointerInputChange = awaitFirstDown().also {
                        motionEvent = actionDown
                        currentPosition = it.position
                    }
                    do {
                        val event: PointerEvent = awaitPointerEvent()

                        var eventChanges =
                            "DOWN changedToDown: ${down.changedToDown()} changedUp: ${down.changedToUp()}\n"
                        event.changes
                            .forEachIndexed { index: Int, pointerInputChange: PointerInputChange ->
                                eventChanges += "Index: $index, id: ${pointerInputChange.id}, " +
                                        "changedUp: ${pointerInputChange.changedToUp()}" +
                                        "pos: ${pointerInputChange.position}\n"

                                pointerInputChange.consumePositionChange()
                            }

                        //gestureText = "EVENT changes size ${event.changes.size}\n" + eventChanges

                        //gestureColor = Color.Green
                        motionEvent = actionMove
                        currentPosition = event.changes.first().position
                    } while (event.changes.any { it.pressed })

                    motionEvent = actionUp
                    //canvasColor = Color.LightGray

                    //gestureText += "UP changedToDown: ${down.changedToDown()} " + "changedUp: ${down.changedToUp()}\n"
                }
            }

        }

    Canvas(
        modifier = drawModifier
            .padding(20.dp)
            .size(500.dp)
    ) {
        val canvasWidth = size.width
        val canvasHeight = size.height
        val line = 1.5
        val squareSize = canvasWidth/line

        drawRect(
            color = Color.White,
            topLeft = Offset(center.x - canvasWidth / 3, center.y - canvasHeight / 6),
            size = Size(width = squareSize.toFloat(), squareSize.toFloat()),
            style = Stroke(
                width = 50.dp.toPx()
            ),
        )

        when(motionEvent){
            actionDown->{
                path.moveTo(currentPosition.x,currentPosition.y)
            }
            actionMove->{
                if (currentPosition!= Offset.Unspecified){
                    path.lineTo(currentPosition.x,currentPosition.y)

                }
            }
            actionUp->{
                path.lineTo(currentPosition.x,currentPosition.y)
                motionEvent = actionIdle

            }
            else-> Unit
        }

       drawPath(
           color = Color.Cyan,
           path = path,
           style = Stroke(width = 5.dp.toPx(), join = StrokeJoin.Round)
       )

    }

}

1voto

Thracian Points 1602

Vous pouvez soit obtenir Rect de votre chemin en utilisant path.getBounds() et le compare à la position tactile actuelle de l'utilisateur. J'ai ajouté ici un exemple pour cela. Je ne vérifie pas s'il y a eu une erreur ou s'il s'est terminé en une touche, vous pouvez le faire. Celui-ci vérifie dans quelle limite nous sommes actuellement, si nous sommes dans le rectangle vert, nous sommes dans des limites correctes.

@Composable
private fun CanvasShapeSample() {

    // This is motion state. Initially or when touch is completed state is at MotionEvent.Idle
    // When touch is initiated state changes to MotionEvent.Down, when pointer is moved MotionEvent.Move,
    // after removing pointer we go to MotionEvent.Up to conclude drawing and then to MotionEvent.Idle
    // to not have undesired behavior when this composable recomposes. Leaving state at MotionEvent.Up
    // causes incorrect drawing.
    var motionEvent by remember { mutableStateOf(MotionEvent.Idle) }
    // This is our motion event we get from touch motion
    var currentPosition by remember { mutableStateOf(Offset.Unspecified) }
    // This is previous motion event before next touch is saved into this current position
    var previousPosition by remember { mutableStateOf(Offset.Unspecified) }

    val innerPath = remember { Path() }
    val outerPath = remember { Path() }

    // Path is what is used for drawing line on Canvas
    val path = remember { Path() }

    var isError by remember { mutableStateOf(false) }

    val drawModifier = Modifier
        .fillMaxSize()
        .background(Color.LightGray)
        .pointerMotionEvents(
            onDown = { pointerInputChange: PointerInputChange ->
                currentPosition = pointerInputChange.position
                motionEvent = MotionEvent.Down
                pointerInputChange.consume()
            },
            onMove = { pointerInputChange: PointerInputChange ->
                currentPosition = pointerInputChange.position
                motionEvent = MotionEvent.Move
                pointerInputChange.consume()
            },
            onUp = { pointerInputChange: PointerInputChange ->
                motionEvent = MotionEvent.Up
                pointerInputChange.consume()
            },
            delayAfterDownInMillis = 25L
        )

    Canvas(modifier = drawModifier) {

        val canvasWidth = size.width
        val canvasHeight = size.height

        val outerShapeWidth = canvasWidth * .8f
        val innerShapeWidth = canvasWidth * .6f

        if (innerPath.isEmpty) {

            innerPath.addRect(
                Rect(
                    offset = Offset(
                        (canvasWidth - innerShapeWidth) / 2,
                        (canvasHeight - innerShapeWidth) / 2
                    ),
                    size = Size(innerShapeWidth, innerShapeWidth)
                )
            )
        }

        if (outerPath.isEmpty) {
            outerPath.addRect(
                Rect(
                    offset = Offset(
                        (canvasWidth - outerShapeWidth) / 2,
                        (canvasHeight - outerShapeWidth) / 2
                    ),
                    size = Size(outerShapeWidth, outerShapeWidth)
                )
            )
        }

        when (motionEvent) {
            MotionEvent.Down -> {
                path.moveTo(currentPosition.x, currentPosition.y)
                previousPosition = currentPosition
                isError = !isInBound(innerPath = innerPath, outerPath = outerPath, currentPosition)
            }

            MotionEvent.Move -> {
                path.quadraticBezierTo(
                    previousPosition.x,
                    previousPosition.y,
                    (previousPosition.x + currentPosition.x) / 2,
                    (previousPosition.y + currentPosition.y) / 2

                )
                previousPosition = currentPosition
                isError = !isInBound(innerPath = innerPath, outerPath = outerPath, currentPosition)
            }

            MotionEvent.Up -> {
                path.lineTo(currentPosition.x, currentPosition.y)
                currentPosition = Offset.Unspecified
                previousPosition = currentPosition
                motionEvent = MotionEvent.Idle
            }

            else -> Unit
        }

        drawPath(color = Color.Green, path = outerPath)
        drawPath(color = Color.Yellow, path = innerPath)

        drawPath(
            color = Color.Red,
            path = path,
            style = Stroke(width = 4.dp.toPx(), cap = StrokeCap.Round, join = StrokeJoin.Round)
        )

        drawCircle(
            color = if (isError) Color.Red else Color.Green,
            center = Offset(100f, 100f),
            radius = 50f
        )
    }
}

private fun isInBound(innerPath: Path, outerPath: Path, position: Offset): Boolean {
    val innerRect = innerPath.getBounds()
    val outerRect = outerPath.getBounds()

    return !innerRect.contains(position) && outerRect.contains(position)
}

Résultat

enter image description here

Si votre forme est complexe, vous pouvez obtenir des segments de chemin et vérifier s'ils sont en dehors des limites de votre forme complexe.

        val segments: Iterable<PathSegment> = path.asAndroidPath().flatten()

pathSegment a un début et une fin PointF valeurs. Si l'utilisateur déplace rapidement le pointeur, il se peut qu'il ne crée pas assez de pathSegments, mais ce serait probablement un cas limite.

Ce tutoriel contient une section sur les segments de chemin et l'exemple ci-dessus donne une idée. Mais cela sera probablement très difficile pour les formes complexes, ce qui pourrait vous obliger à poser la question de l'algorithme pour la détection si une position est à l'intérieur d'un chemin.

https://github.com/SmartToolFactory/Jetpack-Compose-Tutorials#6-1-5-canvas-path-segments

enter image description here

Je vois que vous utilisez mon code pour dessiner sur le canevas que j'ai mentionné. ici . Je l'ai simplifié, vous pouvez vérifier ces gestes pour voir à quel point c'est simple maintenant. Vous n'avez pas besoin de tout ce code.

Prograide.com

Prograide est une communauté de développeurs qui cherche à élargir la connaissance de la programmation au-delà de l'anglais.
Pour cela nous avons les plus grands doutes résolus en français et vous pouvez aussi poser vos propres questions ou résoudre celles des autres.

Powered by:

X