Exploring Custom Drawing and Zooming in Android with Custom Map (Bezier Curve)
7 mins read

Exploring Custom Drawing and Zooming in Android with Custom Map (Bezier Curve)

In the world of Android development, creating interactive and visually appealing user interfaces is a fundamental task. Whether you’re building a drawing app or a map navigation feature, the ability to customize and respond to user input is essential. This is where custom views, like the CustomMapView class, come into play.

In this blog post, we’ll delve into the code of a custom Android view class named CustomMapView. This class allows users to draw curves on the screen and supports zooming functionalities. We’ll break down the code function by function to understand how it works.

Understanding the CustomMapView Class

The CustomMapView class is a custom Android View that extends View and implements the View.OnTouchListener interface. It serves as the canvas where users can draw curves with their fingers. Let’s explore its functions step by step.

Constructors

CustomMapView offers three constructors, enabling instantiation with varying argument combinations. These constructors allow you to create an instance of CustomMapView programmatically, and they all call the init() method to set up the view.

Initialization Method (init())

The init() method initializes the CustomMapView. It performs the following tasks:

  1. Configure Paint Objects: The code sets up two Paint objects, paint and paint1, which are used for configuring the style of the drawing. paint defines a color, stroke width, and style for the drawing path, while paint1 specifies the color and style for filling.
  2. Initialize Path: A Path object, path, is created to represent the drawing path.
  3. Accessibility and Interaction Settings: The view is set as focusable, focusable in touch mode, and clickable. These settings ensure that the view can receive touch events and focus when necessary.
  4. Update Corner Points: The update() method is called to set up corner points, which define the boundaries for drawing on the screen.
  5. Touch Event Registration: The view is set to listen for touch events through setOnTouchListener(this).
 private fun update() {
        leftBottom = Point(0, screenHeight);
        leftTop = Point(0, 0);
        rightBottom = Point(screenWidth, screenHeight);
        rightTop = Point(screenWidth, 0);
    }

The update() method calculates and sets the corner points of the screen, which are represented by leftBottom, leftTop, rightBottom, and rightTop. These points define the screen’s boundaries for drawing.

private fun drawInit() {
        startX = leftTop.x.toFloat()
        startY = leftTop.y.toFloat()
        endX = leftBottom.x.toFloat()
        endY = leftBottom.y.toFloat()
        control1X = x
        control1Y = y
        control2X = x
        control2Y = y
        path!!.moveTo(x, y)
        isDrawing = true
    }

The drawInit() method sets up the initial parameters for drawing. It initializes startX, startY, endX, endY, control1X, control1Y, and control2X as well as sets the isDrawing flag to true.

private fun drawOn(isLeft: Boolean, x: Float, y: Float) {
        if (isDrawing && isLeft) {
            // Update the control points and end points
            startX = leftTop.x.toFloat()
            startY = leftTop.y.toFloat()
            control1X = (x + startX + 200) / 2
            control1Y = y// startY
            control2X = (x + startX+ 200) / 2
            control2Y = y
            endX = leftBottom.x.toFloat()
            endY = leftBottom.y.toFloat()

            // Redraw the curve
            path!!.reset()
            path!!.moveTo(startX, startY)
            path!!.cubicTo(control1X, control1Y, control2X, control2Y, endX, endY)
            invalidate()
        } else if (isDrawing) {
            startX = rightTop.x.toFloat()
            startY = rightTop.y.toFloat()
            control1X = (x + startX+ 200) / 3f
            control1Y = y
            control2X = (x + startX+ 200) / 3f
            control2Y = y
            endX = rightBottom.x.toFloat()
            endY = rightBottom.y.toFloat()

            // Redraw the curve
            path!!.reset()
            path!!.moveTo(startX, startY)
            path!!.cubicTo(control1X, control1Y, control2X, control2Y, endX, endY)
            invalidate()
        }
    }

The drawOn() method updates the drawing process based on user input. It has two branches depending on whether the user is drawing on the left or right side of the screen. It calculates control points and endpoints for a Bezier curve and redraws the path accordingly. This allows users to draw curves on the canvas.

private fun drawOff() {
        if (isDrawing) {
            // Reset the path
            path!!.reset()
            isDrawing = false
            oldDownPressX = -1000f;
            invalidate()
        }
    }

The drawOff() method ends the drawing process by resetting the path and setting isDrawing to false.

  override fun draw(canvas: Canvas?) {
        super.draw(canvas)
        path?.let {
            canvas?.drawPath(it, paint!!)
            canvas?.drawPath(it, paint1!!)

        }
    }

The onDraw(canvas: Canvas?) method is overridden to draw the path on the canvas. It uses the paint and paint1 objects to render the path on the canvas.

override fun onSizeChanged(w: Int, h: Int, oldw: Int, oldh: Int) {
super.onSizeChanged(w, h, oldw, oldh)
screenHeight = h
screenWidth = w
update()
}
fun setZoomListener(zoomLevel: CustomTouchListener)
{
zoomLevels =zoomLevel;
}

The onSizeChanged(w: Int, h: Int, oldw: Int, oldh: Int) method is called when the view’s size changes. It updates the screen dimensions (screenHeight and screenWidth) and calls the update() method to adjust the corner points accordingly.

 fun setZoomListener(zoomLevel: CustomTouchListener)
    {
        zoomLevels =zoomLevel;
    }

Setting a Zoom Listener (setZoomListener(zoomLevel: CustomTouchListener))

The setZoomListener(zoomLevel: CustomTouchListener) method allows you to set a zoom listener, possibly for zooming functionality in your application.

interface CustomTouchListener {
    fun onZoomLevel(level:Float,action:String,yValue:Float)
}
override fun onTouch(v: View?, event: MotionEvent): Boolean {
        val x = event.x
        val y = event.y
        when (event.action) {
            MotionEvent.ACTION_DOWN -> {
                var accessDownPoint = screenWidth / 4
                if (accessDownPoint > event.x || (screenWidth - accessDownPoint) < event.x) {

                    oldDownPressX = event.x
                    oldDownPressY = event.y
                }else
                {
                    oldDownPressX = -1000f
                    oldDownPressY = event.y
                }
                drawInit()
            }
            MotionEvent.ACTION_MOVE -> {

                var result = false
                if (oldDownPressX == -1000f) {
                    return false
                }
                try {
                    val diffX: Float = oldDownPressX - x
                    if (abs(diffX) > SWIPE_THRESHOLD) {
                        if (diffX > 0) {
                            drawOn(false, x, y)
                            zoomLevels.onZoomLevel(y,"DrawLeft",screenHeight.toFloat())
                        } else {
                            drawOn(true, x, y)
                            zoomLevels.onZoomLevel(y,"DrawLeft",screenHeight.toFloat())
                        }
                        result = true
                    }
                } catch (exception: Exception) {
                    exception.printStackTrace()
                }
                return result
            }
            MotionEvent.ACTION_UP -> drawOff();
        }
        return false
    }

The onTouch(v: View?, event: MotionEvent): Boolean method is responsible for handling touch events on the view. It detects touch events like ACTION_DOWN, ACTION_MOVE, and ACTION_UP. The code inside this method is complex and includes logic for tracking user touches, drawing curves, and handling zooming functionality based on user gestures.

Summary

In this blog post, we explored the CustomMapView class, a custom Android view that enables drawing and supports zooming. This class demonstrates the power of custom views and how they can be used to create interactive and visually engaging features in Android applications. Whether you’re building a drawing app, a map navigation tool, or any other application that requires custom graphics and user interactions, understanding the inner workings of custom views like CustomMapView can be a valuable asset in your Android development journey.

Leave a Reply

Your email address will not be published. Required fields are marked *