Introduction
This Android Kotlin tutorial demonstrates how to create a circular bitmap with a border using a custom extension function. The example covers extracting a bitmap image from the assets
folder, transforming it into a circular shape, and adding a border around it. This is particularly useful for displaying profile pictures, icons, or thumbnails in a circular form, which is a common design pattern in modern Android apps.
In this example, the image manipulation is done using the Canvas
and Paint
classes from Android's graphics library, along with a couple of helper functions. The project also makes use of ConstraintLayout to align and display two ImageViews: one for the original image and the other for the circular, bordered version.
MainActivity Breakdown
The MainActivity
class extends the Activity
class and sets the content view to activity_main.xml
, which contains the layout for the app. The layout includes two ImageView
components and one TextView
to label each image.
Inside onCreate()
, the code first references the ImageView
and TextView
widgets from the XML layout. It then loads a bitmap image named "flower103.jpg" from the assets
folder using the assetsToBitmap()
function, which is defined as an extension function for the Context
class. If the bitmap is successfully retrieved, it is displayed in the first ImageView
in its original form.
The next key step is applying the borderedCircularBitmap()
extension function to crop the bitmap into a circular shape and add a border. This processed bitmap is displayed in the second ImageView
, and the TextView
is updated to describe the transformation that has been applied to the image.
assetsToBitmap() Function
The assetsToBitmap()
function is a helper that retrieves a bitmap image from the app's assets
folder. It takes the filename as an argument and uses a try-catch
block to handle potential IOException
errors. If successful, it returns a decoded bitmap using BitmapFactory.decodeStream()
. If it encounters an error, it logs the exception and returns null.
This function simplifies loading images stored within the app's resources, providing a clean way to access asset images without having to write repetitive code.
borderedCircularBitmap() Function
The borderedCircularBitmap()
function is the core of this example. It converts a square bitmap into a circular one with an optional border. This function operates by creating a new bitmap of the same dimensions as the original. A Canvas
is then used to draw the circular shape, and the Path
class defines the border and the circular crop region.
To draw the border, the function first creates a circular path corresponding to the radius of the bitmap and uses clipPath()
to constrain the drawing to this path. The border is drawn using the drawColor()
method, which fills the clipped circular area with the specified border color.
The function then creates another circular path, slightly smaller than the first to account for the border width, and clips the bitmap again to ensure the image is drawn inside this smaller circle. The bitmap is then drawn onto the canvas twice: once to clear the drawing area and once to overlay the actual image, preserving transparency.
Finally, the function crops the newly created circular bitmap to the exact size of the circle and returns the result. This method ensures the final bitmap is centered and has a clean circular border.
Layout Overview
The activity_main.xml
file defines the UI layout using a ConstraintLayout
for flexible positioning. It includes two ImageView
elements for displaying the original and processed bitmaps, along with corresponding TextView
labels.
The layout ensures that both images are centered horizontally on the screen and have consistent padding. The second ImageView
is placed below the first with adequate spacing to provide a clear distinction between the original and circular bordered images.
Summary
This example demonstrates how to create a circular bitmap with a border in Android using Kotlin. By leveraging extension functions, the code is modular and reusable, making it easier to implement similar functionality in other parts of an app. The Canvas
and Path
classes from Android's graphics API are used to handle the drawing and clipping operations, while the custom assetsToBitmap()
and borderedCircularBitmap()
functions simplify the process of loading and transforming images.
The final layout is minimalistic, but it clearly illustrates how to present both the original and modified images on the screen, making it a great starting point for developers looking to implement circular image views in their apps.
package com.cfsuman.kotlintutorials
import android.app.Activity
import android.content.Context
import android.graphics.*
import android.os.Bundle
import android.widget.*
import java.io.IOException
import kotlin.math.min
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 imageView2 = findViewById<ImageView>(R.id.imageView2)
val textView2 = findViewById<TextView>(R.id.textView2)
// get the bitmap from assets folder
val bitmap = assetsToBitmap("flower103.jpg")
bitmap?.apply {
// show original bitmap in first image view
imageView.setImageBitmap(this)
// crop circular area from bitmap and add border
imageView2.setImageBitmap(borderedCircularBitmap(
borderColor = Color.parseColor("#F8F8F8"),
borderWidth = 12
))
textView2.text = "Circular Bitmap (Border #F8F8F8 12 px)"
}
}
}
// extension function to get bitmap from assets
fun Context.assetsToBitmap(fileName:String):Bitmap?{
return try {
val stream = assets.open(fileName)
BitmapFactory.decodeStream(stream)
} catch (e: IOException) {
e.printStackTrace()
null
}
}
// extension function to create circular bitmap with border
fun Bitmap.borderedCircularBitmap(
borderColor:Int = Color.LTGRAY,
borderWidth:Int = 10
):Bitmap?{
val bitmap = Bitmap.createBitmap(
width, // width in pixels
height, // height in pixels
Bitmap.Config.ARGB_8888
)
// canvas to draw circular bitmap
val canvas = Canvas(bitmap)
// get the maximum radius
val radius = min(width/2f,height/2f)
// create a path to draw circular bitmap border
val borderPath = Path().apply {
addCircle(
width/2f,
height/2f,
radius,
Path.Direction.CCW
)
}
// draw border on circular bitmap
canvas.clipPath(borderPath)
canvas.drawColor(borderColor)
// create a path for circular bitmap
val bitmapPath = Path().apply {
addCircle(
width/2f,
height/2f,
radius - borderWidth,
Path.Direction.CCW
)
}
canvas.clipPath(bitmapPath)
val paint = Paint().apply {
xfermode = PorterDuffXfermode(PorterDuff.Mode.CLEAR)
isAntiAlias = true
}
// clear the circular bitmap drawing area
// it will keep bitmap transparency
canvas.drawBitmap(this,0f,0f,paint)
// now draw the circular bitmap
canvas.drawBitmap(this,0f,0f,null)
val diameter = (radius*2).toInt()
val x = (width - diameter)/2
val y = (height - diameter)/2
// return cropped circular bitmap with border
return Bitmap.createBitmap(
bitmap, // source bitmap
x, // x coordinate of the first pixel in source
y, // y coordinate of the first pixel in source
diameter, // width
diameter // height
)
}
<?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:layout_width="match_parent"
android:layout_height="match_parent"
android:id="@+id/rootLayout"
android:background="#DCDCDC"
android:padding="24dp">
<ImageView
android:id="@+id/imageView"
android:layout_width="0dp"
android:layout_height="250dp"
android:layout_marginTop="8dp"
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="Original Bitmap"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toStartOf="@+id/imageView"
app:layout_constraintTop_toBottomOf="@+id/imageView" />
<ImageView
android:id="@+id/imageView2"
android:layout_width="0dp"
android:layout_height="250dp"
android:layout_marginTop="24dp"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toBottomOf="@+id/textView" />
<TextView
android:id="@+id/textView2"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginTop="8dp"
android:text="Circular Bitmap With Border"
app:layout_constraintEnd_toEndOf="@+id/imageView2"
app:layout_constraintStart_toStartOf="@+id/imageView2"
app:layout_constraintTop_toBottomOf="@+id/imageView2" />
</androidx.constraintlayout.widget.ConstraintLayout>
- android kotlin - BitmapShader example
- android kotlin - Bitmap crop rectangle
- android kotlin - Bitmap crop square
- android kotlin - Rounded corners bitmap
- android kotlin - Bitmap crop circular area
- android kotlin - Bitmap add border
- android kotlin - Bitmap to base64 string
- android kotlin - Bitmap to byte array
- android kotlin - Bitmap to grayscale
- android kotlin - Display logo on ActionBar
- android kotlin - ActionBar title style
- android kotlin - ActionBar title padding left
- android kotlin - CalendarView set date
- android kotlin - CalendarView example
- android kotlin - ConstraintLayout center in parent programmatically