Android Kotlin: How to draw a point on a Canvas

Introduction

This code demonstrates how to draw a point (or dot) on a canvas in an Android application written in Kotlin. The code utilizes a custom view with an ImageView to display the drawn point. It also includes SeekBars and a CheckBox to allow users to interact and modify the point's appearance.

Breakdown

The code consists of two main parts: the MainActivity.kt file handling the Activity logic and the drawPoint function for drawing the point on a canvas.

  • MainActivity.kt

    • This file defines the MainActivity class which extends the Activity class.
    • In the onCreate method, the code:
      • Initializes references to UI elements like the ImageView, SeekBars, TextView, and CheckBox.
      • Sets initial values for SeekBar ranges and progress.
      • Attaches a listener to each SeekBar to update the drawing whenever the progress changes.
      • Sets a listener for the CheckBox to update the drawing based on its checked state (round or square point).
      • Calls the updateDrawing function to draw the point initially.
    • The setSeekBarListener function sets a generic listener for all SeekBars to call updateDrawing whenever the progress is changed.
    • The updateDrawing function:
      • Calls the drawPoint function to create a new Bitmap with the point drawn based on SeekBar values and CheckBox state.
      • Updates the TextView content to display the current point width and coordinates.
      • Sets the newly created Bitmap to the ImageView.
  • drawPoint(x, y, strokeWidth, isRound)

    • This function takes optional parameters for X and Y coordinates, stroke width, and a boolean flag for roundness.
    • It creates a new Bitmap with a specified size and configures a Canvas for drawing on it.
    • A Paint object is created to define the point's style (anti-aliasing, color, stroke width, and cap shape based on the isRound flag).
    • Finally, the canvas.drawPoint method draws the point on the canvas using the configured Paint object.
    • The function returns the created Bitmap with the drawn point.

Summary

This code provides a user-interactive way to draw and customize a point on a canvas in an Android app. Users can adjust the point's position, size, and shape using the SeekBars and CheckBox. The drawPoint function encapsulates the logic for drawing the point with various configurations.


MainActivity.kt

package com.cfsuman.kotlintutorials

import android.app.Activity
import android.graphics.*
import android.os.Bundle
import android.widget.CheckBox
import android.widget.ImageView
import android.widget.SeekBar
import android.widget.TextView


class MainActivity : Activity() {
    lateinit var imageView:ImageView
    lateinit var seekBarWidth:SeekBar
    lateinit var seekBarX:SeekBar
    lateinit var seekBarY:SeekBar
    lateinit var textView:TextView
    lateinit var checkBox:CheckBox

    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)
        seekBarWidth = findViewById(R.id.seekBarWidth)
        seekBarX = findViewById(R.id.seekBarX)
        seekBarY = findViewById(R.id.seekBarY)
        textView = findViewById(R.id.textView)
        checkBox = findViewById(R.id.checkBox)


        // initial settings
        seekBarX.max = 1500
        seekBarY.max = 850
        seekBarWidth.max = 100


        seekBarX.progress = 750
        seekBarY.progress = 425
        seekBarWidth.progress = 30


        setSeekBarListener(seekBarWidth)
        setSeekBarListener(seekBarX)
        setSeekBarListener(seekBarY)


        checkBox.setOnCheckedChangeListener {
                buttonView, isChecked ->
            updateDrawing()
        }

        // show initial drawing on image view
        updateDrawing()
    }



    private fun setSeekBarListener(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?) {
                }
            })
    }



    // function to update drawing
    private fun updateDrawing(){
        val bitmap= drawPoint(
            seekBarX.progress.toFloat(),
            seekBarY.progress.toFloat(),
            seekBarWidth.progress + 20F,
            checkBox.isChecked
        )


        textView.text = "Width : ${seekBarWidth.progress + 20} px"

        textView.append(" (X:${seekBarX.progress}, " +
                "Y:${seekBarY.progress})")


        imageView.setImageBitmap(bitmap)
    }
}



// function to draw point / dot on canvas
fun drawPoint(
    x : Float = 500F,
    y : Float = 200F,
    strokeWidth : Float = 50F,
    isRound:Boolean = false
):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 point / dot on canvas
    val paint = Paint().apply {
        isAntiAlias = true
        color = Color.parseColor("#333399")

        // point / dot width
        this.strokeWidth = strokeWidth
        style = Paint.Style.STROKE

        // make it round or square shape
        strokeCap = if (isRound){
            Paint.Cap.ROUND
        }else{
            Paint.Cap.SQUARE
        }
    }


    // finally, draw point / dot on canvas
    canvas.drawPoint(
        x,
        y,
        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" />

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

    <CheckBox
        android:id="@+id/checkBox"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:layout_marginTop="8dp"
        android:text="Round?"
        app:layout_constraintEnd_toEndOf="parent"
        app:layout_constraintStart_toStartOf="parent"
        app:layout_constraintTop_toBottomOf="@+id/textView" />

    <SeekBar
        android:id="@+id/seekBarWidth"
        android:layout_width="0dp"
        android:layout_height="wrap_content"
        android:layout_marginStart="24dp"
        android:layout_marginEnd="24dp"
        app:layout_constraintEnd_toEndOf="parent"
        app:layout_constraintStart_toStartOf="parent"
        app:layout_constraintTop_toBottomOf="@+id/checkBox" />

    <SeekBar
        android:id="@+id/seekBarX"
        android:layout_width="0dp"
        android:layout_height="wrap_content"
        android:layout_marginStart="8dp"
        android:layout_marginTop="24dp"
        android:layout_marginEnd="8dp"
        app:layout_constraintEnd_toEndOf="parent"
        app:layout_constraintStart_toStartOf="parent"
        app:layout_constraintTop_toBottomOf="@+id/seekBarWidth" />

    <SeekBar
        android:id="@+id/seekBarY"
        android:layout_width="0dp"
        android:layout_height="wrap_content"
        android:layout_marginStart="8dp"
        android:layout_marginEnd="8dp"
        app:layout_constraintEnd_toEndOf="parent"
        app:layout_constraintStart_toStartOf="parent"
        app:layout_constraintTop_toBottomOf="@+id/seekBarX" />

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