1

The code below is working fine apart from the fact that once I have dragged my rectangles, I can only select them again by touching the area where they were before I moved them. I don't know how to update their position once I have dragged them. I couldn't find how to do it in the doc, but maybe I was not looking in the right place (androidx.compose.foundation.gestures).

So this is the code that I am using so far:

var offsetX by remember { mutableStateOf(0f) }
var offsetY by remember { mutableStateOf(0f) }
var offsetX2 by remember { mutableStateOf(0f) }
var offsetY2 by remember { mutableStateOf(0f) }

val rect1 = RectF(offsetX, offsetY, offsetX + 200f, offsetY + 300f)
val rect2 = RectF(offsetX2, offsetY2, offsetX2 + 300f, offsetY2 + 400f)
var selectedRect: RectF? = null
val collision = RectF.intersects(rect1, rect2)
val imageBitmap = ImageBitmap(
    1000, 1000, ImageBitmapConfig.Argb8888, false,
    Color.Black.colorSpace
)
val imageBitmapCanvas = Canvas(imageBitmap)
val canvas = Canvas(imageBitmapCanvas.nativeCanvas)
val paint = Paint()
val rectanglePaint = Paint().apply {
    color = android.graphics.Color.BLUE
    style = Paint.Style.STROKE
    strokeWidth = 8f
}

Column(
    modifier = Modifier
        .background(color = Color.DarkGray)
        .padding(16.dp), horizontalAlignment = Alignment.CenterHorizontally
) {
    TextField(
        modifier = Modifier
            .fillMaxWidth()
            .padding(bottom = 30.dp),
        value = textState.value,
        onValueChange = { textState.value = it }
    )
    CanvasDrawScope().draw(Density(1.0f), LayoutDirection.Ltr, canvas,
        Size(1000f, 1000f), ) {
        drawRect(
            topLeft = Offset(0f, 0f), color = if (collision) Color.Red else Color.Green,
            size = Size(1000f, 1000f)
        )
    }

        canvas.nativeCanvas.drawRect(rect1, rectanglePaint)
        canvas.nativeCanvas.drawRect(rect2, rectanglePaint)


    Image(bitmap = imageBitmap, "New Image", Modifier
        .pointerInput(Unit) {
            detectTapGestures(
                onPress = {
                    val x = it.x
                    val y = it.y

                    selectedRect = when {
                        rect1.contains(x, y) -> rect1
                        rect2.contains(x, y) -> rect2
                        else -> null
                    }
                },
            )
        }
        .pointerInput(Unit) {
            detectDragGestures { change, dragAmount ->
                change.consumeAllChanges()
                if (selectedRect == rect1) {
                    offsetX += dragAmount.x
                    offsetY += dragAmount.y
                } else {
                    offsetX2 += dragAmount.x
                    offsetY2 += dragAmount.y
                }

            }
        })

I would be grateful for any ideas.

Gabriele Mariotti
  • 320,139
  • 94
  • 887
  • 841
Code Poet
  • 6,222
  • 2
  • 29
  • 50
  • Just to explain why I am not using the Canvas composable; it is because eventually the rectangles will be words, and there is no drawText in Compose Canvas. – Code Poet Apr 10 '21 at 11:16
  • About the text, in the `Canvas` you can use `drawIntoCanvas { it.nativeCanvas.drawText(....) }` – Gabriele Mariotti Apr 10 '21 at 11:27
  • Thanks, I am now using a Canvas Composable, which is nice, but the problem persists. It must be something to do with my rectangles then, rather than the pointerInput modifier. – Code Poet Apr 10 '21 at 11:56

1 Answers1

1

I changed something in your code in order to use a Canvas Composable.
In the detectDragGestures I update also the Offset in the selected Rect. I would avoid it but I didn't find a better solution.

data class RectData(
    var size: Size,
    var offset: Offset
)

var offsetX by remember { mutableStateOf(0f) }
var offsetY by remember { mutableStateOf(0f) }
var offsetX2 by remember { mutableStateOf(250f) }
var offsetY2 by remember { mutableStateOf(300f) }
val rectList = mutableListOf<RectData>()
var rectA = RectData(Size(200f,300f), Offset(offsetX, offsetY))
var rectB = RectData(Size(500f,600f), Offset(offsetX2, offsetY2))
rectList.add(rectA)
rectList.add(rectB)

var selectedRect: RectData? by remember { mutableStateOf(null) }

    Canvas(modifier = Modifier
        .fillMaxSize()
        .pointerInput(Unit) {
            detectTapGestures(
                onPress = {
                    val x = it.x
                    val y = it.y

                    selectedRect = null
                    rectList.forEach(){
                        val rect = RectF(
                            it.offset.x,
                            it.offset.y,
                            it.offset.x+it.size.width,
                            it.offset.y + it.size.height
                            )
                        if (rect.contains(x,y)) selectedRect = it
                    }
                },
            )
        }
        .pointerInput(Unit) {
            detectDragGestures { change, dragAmount ->
                change.consumeAllChanges()
                when (selectedRect) {
                    rectA -> {
                        offsetX += dragAmount.x
                        offsetY += dragAmount.y
                        rectA.offset = Offset(offsetX,offsetY) //update the offset
                    }
                    rectB -> {
                        offsetX2 += dragAmount.x
                        offsetY2 += dragAmount.y
                        rectB.offset = Offset(offsetX2,offsetY2) //update the offset
                    }
                }
            }
        }
    ){
        val canvasQuadrantSize = size / 2F
        drawRect(
            topLeft = Offset(0f,0f),
            color = Color.Green,
            size = canvasQuadrantSize
        )
        rectList.forEach(){
            drawRect(
                brush = SolidColor(Color.Blue),
                topLeft = it.offset,
                size = it.size,
                style = Stroke(width = 8f)
            )
        }
    }

enter image description here

Gabriele Mariotti
  • 320,139
  • 94
  • 887
  • 841
  • 1
    Many thanks for going through all this trouble. It definitely works perfectly. I am wondering if the reason it is so complex is that I am using android.graphics.RectF? Would it be more straightforward with simple Compose shapes? In which case would it make more sense for me not to use Compose for my purpose (eventually I will be moving words around the canvas)? Thanks again for sharing your expertise. – Code Poet Apr 10 '21 at 14:00
  • 1
    To be honest, I would have expected that using a data class instead of a RectF, it was updated automatically without forcing the update of the object with the new Offset, but maybe there is something wrong in my code. Said that, handling the gesture in the Canvas Composable is easier than the standard Canvas. – Gabriele Mariotti Apr 10 '21 at 14:08
  • @GabrieleMariotti building on this, is there a way to allow for gesture detection as well as a continuous offsetX and offsetY coordinate feed from some external source? ie. the boxes will continue to move in a loop, unless a drag gesture is detected to update its location. – Stigma Apr 26 '21 at 22:03