Android Kotlin: Draw Arc Between Two Points
This code demonstrates how to draw an arc between two points on an Android Canvas in Kotlin. It provides an interactive interface where users can adjust the positions of the two points using SeekBars, and the drawn arc updates accordingly.
Key Components:
- Activity: The
MainActivity
class manages the UI and logic for drawing the arc. - Widgets:
ImageView
: Displays the generated bitmap with the drawn arc.SeekBar
(x4): Allow users to define the X and Y coordinates of the two points (point & point2).TextView
: Shows the current coordinates of both points.
drawArcBetweenTwoPoints
Function: This function takes twoPoint
objects as input and performs the following actions:- Creates a bitmap with a background color.
- Defines a
Paint
object for drawing shapes on the canvas. - Determines the "top" and "bottom" points based on their Y coordinates.
- Calculates the center point between the top and bottom points.
- Handles special cases for straight vertical and horizontal lines.
- Calculates the rectangle (RectF) that defines the size and position of the arc.
- Determines the starting and sweep angles for the arc based on the points' positions.
- Draws a filled arc and a stroked border around it using
canvas.drawArc
.
Summary
This code provides a user-friendly way to visualize an arc drawn between two points on an Android device. Users can adjust the points' positions dynamically and see the corresponding changes in the arc's shape and location.
MainActivity.kt
package com.cfsuman.kotlintutorials
import android.app.Activity
import android.graphics.*
import android.os.Bundle
import android.widget.ImageView
import android.widget.SeekBar
import android.widget.TextView
import kotlin.math.min
import kotlin.math.max
class MainActivity : Activity() {
lateinit var imageView: ImageView
lateinit var pointX: SeekBar
lateinit var point2X: SeekBar
lateinit var pointY: SeekBar
lateinit var point2Y: SeekBar
lateinit var textView: TextView
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContentView(R.layout.activity_main)
// get the widgets reference from XML layout
imageView = findViewById(R.id.imageView)
pointX = findViewById(R.id.pointX)
pointY = findViewById(R.id.pointY)
point2X = findViewById(R.id.point2X)
point2Y = findViewById(R.id.point2Y)
textView = findViewById(R.id.textView)
pointX.max = 1500
pointY.max = 850
point2X.max = 1500
point2Y.max = 850
pointX.progress = 100
pointY.progress = 100
point2X.progress = 600
point2Y.progress = 500
updateDrawing()
setSeekBarChangeListener(pointX)
setSeekBarChangeListener(pointY)
setSeekBarChangeListener(point2X)
setSeekBarChangeListener(point2Y)
}
private fun setSeekBarChangeListener(seekBar: SeekBar){
seekBar.setOnSeekBarChangeListener(
object: SeekBar.OnSeekBarChangeListener{
override fun onProgressChanged(
seekBar: SeekBar?, progress: Int,
fromUser: Boolean) {
updateDrawing()
}
override fun onStartTrackingTouch(seekBar: SeekBar?) {
}
override fun onStopTrackingTouch(seekBar: SeekBar?) {
}
})
}
private fun updateDrawing(){
val bitmap = drawArcBetweenTwoPoints(
point = Point(pointX.progress,pointY.progress),
point2 = Point(point2X.progress, point2Y.progress)
)
imageView.setImageBitmap(bitmap)
textView.text = "( ${pointX.progress} " +
": ${pointY.progress} )"
textView.append(" ( ${point2X.progress} " +
": ${point2Y.progress} )")
}
}
// function to draw arc between two points on canvas
fun drawArcBetweenTwoPoints(
point: Point = Point(150,400),
point2: Point = Point(650, 400)
):Bitmap?{
val bitmap = Bitmap.createBitmap(
1500,
850,
Bitmap.Config.ARGB_8888
)
// canvas for drawing
val canvas = Canvas(bitmap).apply {
drawColor(Color.parseColor("#A2A2D0"))
}
// paint to draw
val paint = Paint()
// manage points
var topPoint = point
var bottomPoint = point2
if (point.y >= point2.y) {
topPoint = point2
bottomPoint = point
}
var centerPoint = Point(topPoint.x,bottomPoint.y)
// manage strait vertical and horizontal line
var isVerticalLine = false
var isHorizontalLine = false
if (bottomPoint.x == topPoint.x){isVerticalLine = true}
if (bottomPoint.y == topPoint.y){isHorizontalLine = true}
if (isVerticalLine){
val lineLength = bottomPoint.y - topPoint.y
centerPoint = Point(bottomPoint.x,
topPoint.y + lineLength/2)
}
if (isHorizontalLine){
val lineLength = max(topPoint.x,bottomPoint.x) -
min(topPoint.x,bottomPoint.x)
centerPoint = Point(bottomPoint.x
+ lineLength/2, topPoint.y)
}
// mark points
canvas.drawCircle(
topPoint.x.toFloat(),
topPoint.y.toFloat(),
30F,
paint.apply {
color = Color.parseColor("#7BB661")
}
)
canvas.drawCircle(
centerPoint.x.toFloat(),
centerPoint.y.toFloat(),
50F,
paint.apply {
color = Color.parseColor("#483D8B")
}
)
canvas.drawCircle(
bottomPoint.x.toFloat(),
bottomPoint.y.toFloat(),
30F,
paint.apply {
color = Color.parseColor("#FF5470")
}
)
// manage rectF
var xLength = max(centerPoint.x,bottomPoint.x) -
min(centerPoint.x,bottomPoint.x)
var yLength = centerPoint.y - topPoint.y
if (isVerticalLine){
yLength = bottomPoint.y - centerPoint.y
xLength = yLength
}
if (isHorizontalLine){
xLength = centerPoint.x -
min(topPoint.x,bottomPoint.x)
yLength = xLength
}
val rectF = RectF(
centerPoint.x - xLength.toFloat(),
centerPoint.y - yLength.toFloat(),
centerPoint.x + xLength.toFloat(),
centerPoint.y + yLength.toFloat()
)
// manage angle for ark
var startAngle = 270F
var sweepAngle = 90F
if (bottomPoint.x < centerPoint.x){
startAngle = 180F
}
if (isHorizontalLine){
startAngle = 180F
sweepAngle = 180F
}
if (isVerticalLine){
startAngle = 270F
sweepAngle = 180F
}
// finally, draw ark between two points
canvas.drawArc(
rectF,
startAngle,
sweepAngle,
true,
paint.apply {
color = Color.parseColor("#333399")
}
)
val paintStroke = Paint().apply {
isAntiAlias = true
color = Color.parseColor("#F0FFFF")
style = Paint.Style.STROKE
strokeWidth = 10F
}
// draw arc border
canvas.drawArc(
rectF,
startAngle,
sweepAngle,
true,
paintStroke
)
return bitmap
}
activity_main.xml
<?xml version="1.0" encoding="utf-8"?>
<androidx.constraintlayout.widget.ConstraintLayout
xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
android:id="@+id/rootLayout"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:background="#DCDCDC"
android:padding="24dp">
<ImageView
android:id="@+id/imageView"
android:layout_width="0dp"
android:layout_height="wrap_content"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toTopOf="parent" />
<TextView
android:id="@+id/textView"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginTop="8dp"
android:text="TextView"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toBottomOf="@+id/imageView" />
<SeekBar
android:id="@+id/pointX"
android:layout_width="0dp"
android:layout_height="wrap_content"
android:layout_marginTop="8dp"
android:progressTint="#7BB661"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toBottomOf="@+id/textView" />
<SeekBar
android:id="@+id/pointY"
android:layout_width="0dp"
android:layout_height="wrap_content"
android:layout_marginTop="8dp"
android:progressTint="#7BB661"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toBottomOf="@+id/pointX" />
<SeekBar
android:id="@+id/point2X"
android:layout_width="0dp"
android:layout_height="wrap_content"
android:layout_marginTop="24dp"
android:progressTint="#FF5470"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintHorizontal_bias="0.0"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toBottomOf="@+id/pointY" />
<SeekBar
android:id="@+id/point2Y"
android:layout_width="0dp"
android:layout_height="wrap_content"
android:layout_marginTop="8dp"
android:progressTint="#FF5470"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toBottomOf="@+id/point2X" />
</androidx.constraintlayout.widget.ConstraintLayout>
- android kotlin - Canvas draw text on path
- android kotlin - Canvas draw multiple lines
- android kotlin - Canvas draw line
- android kotlin - Canvas draw path
- android kotlin - Context menu example
- android kotlin - CalendarView set minDate maxdate
- android kotlin - Toolbar back button color
- android kotlin - Change circular progress bar color
- android kotlin - Custom horizontal progress bar
- android kotlin - Progressbar with percentage example
- android kotlin - Create ColorStateList programmatically
- android kotlin - ListView add header programmatically
- android kotlin - MaterialCardView border color width radius example
- android kotlin - Material button remove padding example
- android kotlin - AlertDialog single choice items example