I am creating a pixel art app, which has the following layout:
Input events are detected inside the pixel art board, meaning that if the user swipes from the root layout and travels their finger inside the pixel art board, it doesn't detect it. This is obviously a minor issue.
To fix this, I looked online and I found the following code which kind of fixed the problem:
binding.root.setOnTouchListener { _, motionEvent ->
val hitRect = Rect()
binding.activityCanvasCardView.getHitRect(hitRect)
if (hitRect.contains(motionEvent.x.toInt(), motionEvent.y.toInt())) {
Log.d("LOG123", "Hi ${motionEvent.xPrecision} ${motionEvent.yPrecision}")
binding.activityCanvasPixelGridView.onTouchEvent(motionEvent)
}
true
}
Note that the view coordinates are converted into pixel coordinates in the onTouchEvent method.
Simple enough, right? In a perfect world, that code should fix the issue.
There's only one problem, for some reason, there is an offset with the y value:
I am unsure why I am having this strange delay with the Y coordinates.
I've tried to fix this issue, some of the things I tried were:
- manually applying offset values
- using different rect functions of the
Viewclass - look online to see if anyone has a similar issue
I'm following things by the book.
I tried the code that Sergei Kozelko gave me, I don't know if it's because I'm scaling/sizing the view in onCreate, but the code isn't working:
Code I tried:
binding.root.setOnTouchListener { _, motionEvent ->
val hitRect = Rect()
binding.activityCanvasCardView.getHitRect(hitRect)
if (hitRect.contains(motionEvent.x.toInt(), motionEvent.y.toInt())) {
val offsetX = motionEvent.x - binding.activityCanvasPixelGridView.left
val offsetY = motionEvent.y - binding.activityCanvasPixelGridView.top
motionEvent.offsetLocation(offsetX, offsetY)
val inverseCopy = Matrix()
if (!binding.activityCanvasPixelGridView.matrix.isIdentity) {
binding.activityCanvasPixelGridView.matrix.invert(inverseCopy)
motionEvent.transform(inverseCopy)
}
binding.activityCanvasPixelGridView.dispatchTouchEvent(motionEvent)
}
true
}



The problem is likely that you are calling
activityCanvasPixelGridView.onTouchEventwith unmodified parent event. The coordinates are in parent's coordinate space whileonTouchEventexpects them to be in this view's coordinate space.Android does this automatically in
ViewGroup#dispatchTransformedTouchEvent:There is also
getTransformedMotionEventwhich just transforms the event but it is marked private. So you'll have to reimplement it yourself. The only non-straightforward moment is dealing with transformation, as bothhasIdentityMatrixandgetInverseMatrixare marked with@UnsupportedAppUsageand should not be used. Instead you'll need to useMatrix#isIdentityandMatrix#invertwhich do the same calculations, just with more overhead.Combined together it looks like this:
Now, all this works only if
child(activityCanvasCardView) is a direct child ofparent(root). If it is not you'll need to apply the above transformation for every view between them. Again, there is nothing in Android that does exactly what you need, the closest thing isViewGroup#offsetRectIntoDescendantCoords. But it operates onRects and doesn't account for transformations. And again you can implement it yourself like this:and call like
transformToDescendant(binding.root, binding.activityCanvasPixelGridView, event)insidesetOnTouchListener.Please note that this code was not properly tested, throws
ClassCastExceptionifdescendantis not actually a descendant ofparentand maybe could be optimized better to remove list. However it should work, or at least give you general idea how to implement it yourself.Finally, this doesn't happen on X axis because the canvas fills full width, so the X coordinates in parent and child views are the same.