Android Kotlin: How to crop a circle from an ImageView

This code demonstrates how to crop a circular area from an existing image displayed in an ImageView and display the cropped image in another ImageView in an Android application written in Kotlin.

The code utilizes custom extension functions to achieve this functionality. One function retrieves a Bitmap image from the assets folder of the application. The other function performs the core image manipulation - cropping a circular area from the original bitmap.

Breakdown and Summary

The MainActivity.kt file handles the activity lifecycle and UI interaction. It retrieves a bitmap from the assets folder using the assetsToBitmap extension function and displays it in the first ImageView.

The cropCircularArea extension function is the heart of the image manipulation. It takes an optional diameter parameter (defaults to the minimum of width and height) and performs the following steps:

  1. Creates a new bitmap with the same dimensions and configuration (ARGB_8888) as the original.
  2. Creates a canvas for drawing on the new bitmap.
  3. Defines a circular path based on the provided diameter or the minimum dimension of the original image.
  4. Clips the canvas to the defined circular path.
  5. Draws the original bitmap onto the canvas.
  6. Calculates the offset coordinates to extract the desired circular area.
  7. Creates a final cropped bitmap from the new bitmap using the calculated offsets and diameter as width and height.

Finally, the cropped circular image is displayed in the second ImageView, and a text view displays information about the crop radius.


MainActivity.kt

package com.example.jetpack

import android.content.Context
import android.graphics.*
import android.os.Bundle
import androidx.appcompat.app.AppCompatActivity
import kotlinx.android.synthetic.main.activity_main.*
import java.io.IOException
import kotlin.math.min


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

        // get bitmap from assets folder
        val bitmap: Bitmap? = assetsToBitmap("flower9.jpg")

        // show bitmap on first image view
        bitmap?.apply { imageView.setImageBitmap(this) }

        // crop specified size circular area from bitmap
        bitmap?.cropCircularArea(700)?.apply {
            imageView2.setImageBitmap(this)
            textView.text = "Circular image view, crop radius: 350 pixels"
        }
    }
}


// extension function to crop specified size circular area from bitmap
fun Bitmap.cropCircularArea(
    diameter:Int = min(width,height)
):Bitmap?{
    val bitmap = Bitmap.createBitmap(
        width, // width in pixels
        height, // height in pixels
        Bitmap.Config.ARGB_8888
    )

    val canvas = Canvas(bitmap)

    // create a circular path
    val path = Path()
    val length = min(diameter,min(width,height))
    val radius = length / 2F // in pixels
    path.apply {
        addCircle(
            width/2f,
            height/2f,
            radius,
            Path.Direction.CCW
        )
    }

    // draw circular bitmap on canvas
    canvas.clipPath(path)
    canvas.drawBitmap(this,0f,0f,null)

    val x = (width - length)/2
    val y = (height - length)/2

    // return cropped circular bitmap
    return Bitmap.createBitmap(
        bitmap, // source bitmap
        x, // x coordinate of the first pixel in source
        y, // y coordinate of the first pixel in source
        length, // width
        length // height
    )
}


// extension function to get bitmap from assets
fun Context.assetsToBitmap(fileName: String): Bitmap?{
    return try {
        with(assets.open(fileName)){
            BitmapFactory.decodeStream(this)
        }
    } catch (e: IOException) { null }
}
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"
    xmlns:tools="http://schemas.android.com/tools"
    android:id="@+id/constraintLayout"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    android:background="#DCDCDC"
    tools:context=".MainActivity">

    <ImageView
        android:id="@+id/imageView"
        android:layout_width="0dp"
        android:layout_height="260dp"
        android:layout_marginStart="8dp"
        android:layout_marginTop="8dp"
        android:layout_marginEnd="8dp"
        app:layout_constraintEnd_toEndOf="parent"
        app:layout_constraintStart_toStartOf="parent"
        app:layout_constraintTop_toTopOf="parent"
        tools:srcCompat="@tools:sample/avatars" />

    <ImageView
        android:id="@+id/imageView2"
        android:layout_width="0dp"
        android:layout_height="260dp"
        android:layout_marginStart="8dp"
        android:layout_marginTop="16dp"
        android:layout_marginEnd="8dp"
        app:layout_constraintEnd_toEndOf="parent"
        app:layout_constraintStart_toStartOf="parent"
        app:layout_constraintTop_toBottomOf="@+id/imageView"
        tools:srcCompat="@tools:sample/avatars" />

    <TextView
        android:id="@+id/textView"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:layout_marginTop="8dp"
        tools:text="TextView"
        android:fontFamily="sans-serif-condensed-medium"
        style="@style/TextAppearance.AppCompat.Medium"
        app:layout_constraintEnd_toEndOf="@+id/imageView2"
        app:layout_constraintStart_toStartOf="@+id/imageView2"
        app:layout_constraintTop_toBottomOf="@+id/imageView2" />

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