Hi ! I am drawing into my `Composable` , but I don...
# compose
l
Hi ! I am drawing into my
Composable
, but I don't know how to make this drawing reactive to click. More in the thread.
This method is part of my drawing, and I would like to handle clicks with the
clickCallback
handler. But how to make the content of
drawIntoCanvas
react to this callback ?
c
Maybe try collision between touch(x,y) and object(x,y)
👍 1
just do a classical circle collision
l
Thank you : could you either elaborate a bit more or point me to an article/blog ? Do you think about touch from the native canvas ?
c
Elaborating on it, in some games I wrote you would always have a list of objects you wanted to test your collisions against, you wouldn’t bother doing it for everything on the game because you just want some things to cause a reaction.
I believe you can get the touch X Y
l
I can find about collision, I'm sure.
c
100%
👍 1
Collision is very simple, doesn’t need to look for kotlin specific
l
c
you just wanna run this list of objects you want to test a collision agains with something like fun checkIfTouched(touchPosition: Position, listOfCollidableObjects: List<Object>){ forEach(object in listOfCollidableObjects){ if(colided(touchPosition, object.positition)){ object.runYourObjectFunction() } } }
👍 1
something like this
obviously you just wanna run this on something like onScreenTouched or whatever method you have around from the system that tells you when something was touched
And don’t try the circle collision, do it with it, it’s the most efficient in terms of computing cost. Besides that there are some optimizations if you would have too many objects you could colide with. If that would be the case you just need to separate this objects in sectors where each sector has it’s own list of objects so you don’t need to go trough all of them every time.
🔥 2
Hope this helps 🙂
l
Thank you, but I still need a way to react to touch in the native canvas : still investigating around this
c
So, not android?
l
Yes I'm in android, in a Composable item more precisely
c
So getting the touch event in a android compose environment would be correct
l
Unfortunately, this only work if declared inside an activity class, not in a
Drawscope
. After, what I try to achieve may be impossible.
I believe this might solve it
You would need to add
Copy code
Modifier.pointerInput(Unit) {
    detectTapGestures(
        onPress = { /* Called when the gesture starts */ },
        onDoubleTap = { /* Called on Double Tap */ },
        onLongPress = { /* Called on Long Press */ },
        onTap = { /* Called on Tap */ }
    )
}
To your canvas Modifier
👍 1
l
But in my snippet, I just have a
DrawScope
reference, and a
nativeCanvas
call. Again, I may be searching for an impossible use case.
c
If you expect DrawScope to know about touch events then yes, this doesn’t make any sense
👍 1
But
Couldn’t you detect the touch outside
and then pass it as reference to this method?
let’s say you have a reference to your drawScope = DrawScope
l
Not ideal, but I think I should try. Because the snippet give code for a single custom button. And in the main Composable, there may be 4 of these, or not.
c
like you could be listening for touches
Could you pass and instance of click listener into this click function maybe
l
I'll try at least this method. I can't pass the full code as I'm still trying to fix lot of errors and it's long.
c
Awesome
I kinda am interested in this thing
and I will probably do something similar in the future
but check your architecture
👍 1
maybe it’s not correct and you are suffering more getting around this than refactoring
z
What do you actually want to do when a click happens? Are you trying to detect a click inside of some region in your canvas (as the discussion above seems to assume), or trying to change something about what is being drawn depending on the click position?
l
Hi, in the current state of my Composable, I simply need to handle tap gestures, but it does not work :
Copy code
Canvas(
mofifier = Modifier.pointerInput(Unit) {
detectDragGestures(
                    onDragStart = { handleDragStart(it) },
                    onDragCancel = { dndCancelCallback() },
                    onDrag = { change, dragAmount ->
                        change.consumeAllChanges()
                        dndMoveCallback(dragAmount.x, dragAmount.y)
                    },
                    onDragEnd = {
                        dndValidatingCallback()
                    })

                detectTapGestures(
                    onTap = { offset ->
                        handleTap(offset)
                    }
                ))

fun handleTap(offset: Offset) {
        //////////////////////////
        println("Got tap at $offset")
        //////////////////////////
    }
Ok, it seems that I need to set either
detectTapGestures
or
detectDragGestures
z
if you only want tap gestures, then you don’t need
detectDragGestures
, yea
c
Looks awesome, if you can please update us if it works 😛
Copy code
@Composable
@Preview
fun DefaultPreview() {
    Canvas(modifier = Modifier.fillMaxSize().pointerInput(Unit){
        detectTapGestures(
            onTap = {
                println("FRAT")
            }
        )
    }){
    }
}
This is working
l
In fact, I need drag gesture for regular Drag and Drop, and sometimes, when I show promotion buttons (chess board), in only need tap gesture. That's why I'm using both with an if clause
Copy code
Canvas(
        modifier = modifier
            .background(Color(214, 59, 96))
            .pointerInput(Unit) {
                if (dndState.pendingPromotion) {
                    /////////////////////////////
                        println("tap gestures")
                        ////////////////////////////
                    detectTapGestures(
                        onTap = { offset ->
                            handleTap(offset)
                        }
                    )
                }
                else {
                    /////////////////////////
                        println("drag gestures")
                        //////////////////////////
                    detectDragGestures(
                        onDragStart = { handleDragStart(it) },
                        onDragCancel = { dndCancelCallback() },
                        onDrag = { change, dragAmount ->
                            change.consumeAllChanges()
                            dndMoveCallback(dragAmount.x, dragAmount.y)
                        },
                        onDragEnd = {
                            dndValidatingCallback()
                        })
                }
            },
    ) {
...
}
z
You can detect tap and drag gestures simultaneously, you just need to
launch
each call into its own coroutine so they “run” concurrently
👍 1
👍🏽 1
🙏 2
l
Thank you :
PointerInputScope
cannot receive
launch
. Must I use a kind a wrapper ?
Copy code
.pointerInput(Unit) {
                launch {
                    detectTapGestures(
                        onTap = { offset ->
                            handleTap(offset)
                        }
                    )
                }
                launch {
                    detectDragGestures(
                        onDragStart = { handleDragStart(it) },
                        onDragCancel = { dndCancelCallback() },
                        onDrag = { change, dragAmount ->
                            change.consumeAllChanges()
                            dndMoveCallback(dragAmount.x, dragAmount.y)
                        },
                        onDragEnd = {
                            dndValidatingCallback()
                        })
                }
My naive implementation doesn't work.
(I'm also a beginner in coroutines)
z
You might need to wrap them both in a
coroutineScope
👍 1
l
I think I got it :
Copy code
val coroutineScope = rememberCoroutineScope()
...
coroutineScope.launch {
...
}
z
nope, just
Copy code
pointerInput(Unit) {
  coroutineScope {
    launch {
      detectTapGestures(…)
    }
    launch {
      detectDragGestures(…)
    }
  }
}
🙏 1
👍 1
I think that should work
l
Yes, it worked 🙂 Thank you very much 🙂
c
Awesome
l
Hi ! I've got another problem which may be related to this snippet. (Up : @Zach Klippenstein (he/him) [MOD] @Cicero)
It seems that when recomposition happens in the host composable, then captured variables inside the two
launch
and tap/drag gestures are not updated.
I explain : my composable has a parameter
reversed: Boolean
, and the two handlers refer to this
reversed
value. But though the recomposition happens and
reversed
is changed, the behaviour of the two handlers stays the same. I mean it did not take into account the new
reversed
value.
c
Just dump the full code for us :p
Sorry, I think looking from my phone didn’t presented it as expandable. I will look over it tomorrow and try to make it work
👍 1