Introduction
Creating custom UI components in Android can significantly enhance the user experience and visual appeal of your app. One such common UI requirement is displaying circular images with borders. By default, Android’s ImageView
does not provide an easy way to apply circular cropping or add borders. However, by using Kotlin extensions and custom drawing techniques, we can create a reusable, efficient solution to display circular images with borders.
In this tutorial, we will break down how to create a bordered circular ImageView
in an Android application using Kotlin. We will leverage Kotlin extension functions to simplify the process of converting a regular bitmap into a circular image with a border, which can be easily applied across different parts of the app.
Main Code Walkthrough
The MainActivity.kt
file is the core of this implementation. The onCreate
method initializes the UI and loads an image from the app's assets folder. The image is then displayed in two different ImageView
widgets. The first ImageView
shows the raw image, while the second ImageView
applies the circular cropping and border effect using an extension function.
The extension function circularWithBorder
is the key to transforming a regular Bitmap
into a circular one with a border. This function calculates the diameter and radius of the desired circular image by taking into account the original image dimensions and the specified border width. A Canvas
is used to draw the image and apply the circular border. The result is encapsulated in a RoundedBitmapDrawable
, which is then set as the drawable for the ImageView
.
Circular Image with Border Implementation
To create a circular bitmap, the function first calculates the diameter and radius based on the image dimensions. It ensures that the circular crop fits within the bounds of the smallest dimension of the original image. A Canvas
object is then used to draw the bitmap at the center of the canvas. The circular border is drawn using a Paint
object configured with the desired border width, style, and color. This ensures that the image has a neatly drawn circular frame.
The RoundedBitmapDrawableFactory.create
method is used to return a RoundedBitmapDrawable
, which supports setting the image as circular. The result of this operation is a circular image with a smooth, anti-aliased border, which can then be easily assigned to any ImageView
.
Supporting Utility Functions
Two additional Kotlin extension functions are used in this implementation. The dpToPixels
function converts density-independent pixels (dp) to screen pixels, ensuring that the border width scales appropriately across different screen densities. This is essential for creating a visually consistent UI across devices.
The assetsToBitmap
function handles loading images from the app's assets folder. It safely reads an image file, decodes it into a Bitmap
, and returns it for further processing. This function is robust against I/O exceptions, ensuring that the app doesn't crash if the image file is missing or unreadable.
XML Layout
The activity_main.xml
layout defines a simple UI with two ImageView
widgets inside a ConstraintLayout
. The first ImageView
displays the original bitmap image, while the second ImageView
shows the circular version with a border. Both views are constrained within the layout to maintain responsiveness and adapt to different screen sizes.
Summary
In this guide, we demonstrated how to create a circular ImageView
with a customizable border using Kotlin in Android. By leveraging extension functions and custom drawing techniques, we simplified the process of transforming a regular bitmap into a circular image with a border. The approach we used ensures that this functionality can be reused throughout the app.
With these techniques, you can enhance the visual design of your Android applications by displaying circular images in a simple, efficient, and reusable way. Whether you need to show profile pictures, logos, or any other type of image, this method provides a clean, professional solution.
MainActivity.kt
package com.example.jetpack
import android.content.Context
import android.graphics.*
import android.os.Bundle
import android.util.TypedValue
import androidx.appcompat.app.AppCompatActivity
import androidx.core.graphics.drawable.RoundedBitmapDrawable
import androidx.core.graphics.drawable.RoundedBitmapDrawableFactory
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("flower10.jpg")
// show the bitmap on first image view
bitmap?.apply { imageView.setImageBitmap(this) }
// make the bitmap circular with specified border
bitmap?.circularWithBorder(
context = applicationContext,
borderWidth = 7.dpToPixels(applicationContext),
borderColor = Color.parseColor("#004225")
)?.apply {
imageView2.setImageDrawable(this)
}
}
}
// extension function to make a bitmap circular with border
fun Bitmap.circularWithBorder(
context: Context,
borderWidth: Float = 25F,
borderColor: Int = Color.BLACK
): RoundedBitmapDrawable? {
// calculate the bitmap diameter and radius
val diameter = (min(width, height) + borderWidth * 2).toInt()
val radius = diameter / 2F
val bitmap = Bitmap.createBitmap(diameter, diameter, Bitmap.Config.ARGB_8888)
val canvas = Canvas(bitmap)
canvas.drawColor(Color.WHITE)
// draw bitmap at canvas center
canvas.drawBitmap(
this, // bitmap
(diameter - this.width)/2F, // left
(diameter - this.height)/2F, // top
null // paint
)
// pa to draw circular border
Paint().apply {
style = Paint.Style.STROKE
// stroke is always centered so double it
strokeWidth = borderWidth * 2F
color = borderColor
isAntiAlias = true
// draw circular border on canvas
canvas.drawCircle(canvas.width / 2F,canvas.width / 2F,radius,this)
}
// return circular bitmap drawable with border
return RoundedBitmapDrawableFactory.create(context.resources, bitmap).apply {
isCircular = true
setAntiAlias(true)
}
}
// extension function to convert dp to equivalent pixels
fun Int.dpToPixels(context: Context):Float = TypedValue.applyDimension(
TypedValue.COMPLEX_UNIT_DIP, this.toFloat(), context.resources.displayMetrics
)
// 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 }
}
<?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="#EDEAE0"
tools:context=".MainActivity">
<ImageView
android:id="@+id/imageView"
android:layout_width="0dp"
android:layout_height="280dp"
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="280dp"
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" />
</androidx.constraintlayout.widget.ConstraintLayout>
- android kotlin - Create ImageView programmatically
- android kotlin - ImageView rounded corners transparent
- android kotlin - ImageView circle crop
- android kotlin - Circular ImageView programmatically
- android kotlin - ImageView border radius
- android kotlin - ImageView add border programmatically
- android kotlin - ImageView add border
- android kotlin - ImageView rounded corners programmatically
- android kotlin - ImageView set image from drawable
- android kotlin - ImageView set image from url
- android kotlin - Get battery percentage programmatically
- android kotlin - Get battery level programmatically
- android kotlin - Get battery voltage programmatically
- android kotlin - On back button pressed example
- android kotlin - Get string resource by name