Monday, 13 December 2021

Android Canvas: Detecting faster dispatchTouchEvents

I have the following code which creates a pixel grid layout with a spanCount parameter:

class MyCanvasView(context: Context, val spanCount: Double) : View(context) {
    lateinit var extraCanvas: Canvas
    private lateinit var extraBitmap: Bitmap

    val rectangles = mutableListOf<RectF>()

    private lateinit var caller: CanvasFragmentListener

    override fun onSizeChanged(w: Int, h: Int, oldw: Int, oldh: Int) {
        super.onSizeChanged(w, h, oldw, oldh)

        caller = context as CanvasFragmentListener

        if (::extraBitmap.isInitialized) extraBitmap.recycle()

        extraBitmap = Bitmap.createBitmap(width, height, Bitmap.Config.ARGB_8888)
        extraCanvas = Canvas(extraBitmap)

        val scale = (w / spanCount)

        for (i in 0 until spanCount.toInt()) {
            for (i_2 in 0 until spanCount.toInt()) {
                val left = (i * scale).toFloat()
                val top = (i_2 * scale).toFloat()
                val rect = RectF(
                    left,
                    top,
                    left + scale.toFloat(),
                    top + scale.toFloat()
                )
                Log.d("MY_LOG", "LEFT: ${((i * scale).toFloat())} TOP: ${((i_2 * scale).toFloat())} ")

                rectangles.add(rect)
                extraCanvas.drawRect(
                    rect,
                    Paint().apply {
                        style = Paint.Style.FILL
                        color = Color.WHITE
                    })
            }
        }
    }

    private fun drawRectAt(x: Float, y: Float) {
        for (rect in rectangles) {
            if (rect.contains(x, y)) {
                caller.onPixelTapped(this, rect)
                invalidate()
            }
        }
    }

    override fun dispatchTouchEvent(event: MotionEvent): Boolean {
        val x = event.x
        val y = event.y

        when (event.actionMasked) {
            MotionEvent.ACTION_MOVE -> {
                drawRectAt(x, y)
            }
            MotionEvent.ACTION_DOWN -> {
                drawRectAt(x, y)
            }
        }

        return true
    }

    override fun onDraw(canvas: Canvas) {
        super.onDraw(canvas)
        canvas.drawBitmap(extraBitmap, 0f, 0f, null)
    }
}

This canvas can detect swipe/touch events of the user, which means if the user drags their finger from top to bottom at a slow speed they will see the following:

(On the left, I did a very slow bottom-to-top swipe, and on the very right, I did a very fast bottom-to-top swipe. The swipes gradually get faster as you go along.)

enter image description here

As you may tell, the far right example looks very bad! There are many holes in between these pixels. It seems as though the dispatchTouchEvent isn't handling fast gestures so well.

If I open up any conventional pixel art editor and do the same as what I did in my app, there is no difference between how each line is rendered:

enter image description here

If anyone knows how I can achieve this on a Canvas please reply to this - the solution may be easy or it may be obvious - I'm new to Android Canvas so I don't really know. But for my pixel art app I want the user to be able to swipe fast and have it all rendered nicely, but I have a feeling this is going to be hard to implement and will require a lot of low level code, this guy even said you need to move to OpenGL ES to achieve this but Idk if this is true.


I know I could possibly use getHistoricalX() and getHistoricalY() but I have tried to find online examples of how to use it but it is mostly vague and complex. If anyone has any better solution also please share or at least give me an insight as to how I can use getHistoricalX() and getHistoricalY() as it's quite advanced.



from Android Canvas: Detecting faster dispatchTouchEvents

No comments:

Post a Comment