Wednesday, July 8, 2020

android kotlin - Canvas draw arc between two points

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>