Android Kotlin: How to draw dotted line on a Canvas

Android Kotlin - How to draw dotted line on a Canvas

This code demonstrates how to draw a dotted line on a canvas in an Android app written in Kotlin. It utilizes an ImageView to display the line and a SeekBar to control the width of the dots.

Explanation:

  1. MainActivity.kt:

    • The onCreate method sets up the UI elements, including the ImageView and SeekBar.
    • It calls the drawDottedLine function with the initial dot width (progress + 10) to create a bitmap with the dotted line. This bitmap is then set to the ImageView.
    • A SeekBar.OnSeekBarChangeListener is implemented to update the dotted line whenever the user adjusts the seekbar. The listener retrieves the new progress value, generates a new bitmap with the updated dot width, and sets it back to the ImageView.
  2. drawDottedLine function:

    • This function takes an optional dotWidth parameter (default 25f) and creates a new bitmap with a specified size and ARGB_8888 config (supports transparency).
    • It creates a Canvas object from the bitmap and sets a background color.
    • A Paint object is configured with anti-aliasing, desired color, stroke width, round stroke cap, and a DashPathEffect to create the dotted line pattern. The DashPathEffect takes an array of floats defining the lengths of on and off segments for the dotted line. Here, it creates a nearly rounded dot by setting on segment to one-twelfth of the dot width and the off segment to twice the dot width.
    • A Path object defines the line path. It starts at a point and uses a quadratic bezier curve to draw a curved line across the canvas.
    • Finally, the drawPath method on the canvas draws the defined path with the configured paint, resulting in the dotted line on the bitmap.
  3. activity_main.xml:

    • This layout file defines the user interface for the activity. It uses a ConstraintLayout to position the ImageView and SeekBar elements. The ImageView fills the width of the screen and wraps its height to fit the content. The SeekBar is placed below the ImageView and allows the user to adjust the dot width within a range of 0 to 200.

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


class MainActivity : Activity() {
    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)
        setContentView(R.layout.activity_main)

        // get the widgets reference from XML layout
        val imageView = findViewById<ImageView>(R.id.imageView)
        val seekBar = findViewById<SeekBar>(R.id.seekBar)


        // show initial drawing on image view
        imageView.setImageBitmap(
            drawDottedLine(seekBar.progress + 10F)
        )


        // update drawing on seek bar change
        seekBar.setOnSeekBarChangeListener(object :
            SeekBar.OnSeekBarChangeListener{
            override fun onProgressChanged(
                seekBar: SeekBar?, progress: Int, fromUser: Boolean) {

                val bitmap = drawDottedLine(progress + 10F)
                imageView.setImageBitmap(bitmap)
            }

            override fun onStartTrackingTouch(seekBar: SeekBar?) {
            }

            override fun onStopTrackingTouch(seekBar: SeekBar?) {
            }
        })
    }
}



// function to draw dotted line on canvas
fun drawDottedLine(
    dotWidth : Float = 25F
):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 dotted line
    val paint = Paint().apply {
        isAntiAlias = true
        color = Color.parseColor("#333399")

        strokeWidth = dotWidth
        style = Paint.Style.STROKE
        strokeCap = Paint.Cap.ROUND

        pathEffect = DashPathEffect(
            // try to make nearly rounded shape / dot
            floatArrayOf
                (dotWidth / 12,
                dotWidth * 2
            ),
            0F // phase
        )
    }


    // draw a line path
    val path = Path().apply {
        // move to line starting point
        moveTo(100F,canvas.height / 2F)

        // draw line
        // add a quadratic bezier
        quadTo(
            100F, // x1
            canvas.height / 2F, // y1
            canvas.width - 100F, // x2
            canvas.height / 2F // y2
        )
    }


    // finally, draw the path (dotted line) on canvas
    canvas.drawPath(path, paint)

    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" />

    <SeekBar
        android:id="@+id/seekBar"
        android:layout_width="0dp"
        android:layout_height="wrap_content"
        android:max="200"
        android:progress="20"
        android:layout_marginTop="16dp"
        app:layout_constraintEnd_toEndOf="parent"
        app:layout_constraintStart_toStartOf="parent"
        app:layout_constraintTop_toBottomOf="@+id/imageView" />

</androidx.constraintlayout.widget.ConstraintLayout>
More android kotlin tutorials