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.
- Android
- Data Analysis
- Exploring Java OOP: Understanding Object-Oriented Programming
- Flutter
- IOS
- Mobile Application
- Project Management
- Stay focused on your goal, even when every dog barks
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:
- Configure Paint Objects: The code sets up two
Paint
objects,paint
andpaint1
, which are used for configuring the style of the drawing.paint
defines a color, stroke width, and style for the drawing path, whilepaint1
specifies the color and style for filling. - Initialize Path: A
Path
object,path
, is created to represent the drawing path. - 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.
- Update Corner Points: The
update()
method is called to set up corner points, which define the boundaries for drawing on the screen. - 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.