Android Kotlin: Custom Design Circular Progress Bar
This code demonstrates how to create a custom circular progress bar in an Android application using Kotlin. It achieves this by defining a custom drawable resource for the progress bar and controlling its progress within the main activity.
Key functionalities:
- The
circular_progress_bar.xml
drawable defines the visual appearance of the progress bar. It uses a layer-list to combine a background oval with a colored progress ring. The ring utilizes a sweep gradient for a smooth color transition. - The
MainActivity.kt
class handles user interaction and progress updates. It simulates downloading a random number of files. - Upon clicking the "Start Task" button, the progress bar resets and a new download task is initiated on a separate thread.
- The download progress is simulated using a loop that increments a counter with a slight delay.
- The
Handler
ensures updates to the progress bar and text views happen on the main UI thread. - The text views display the downloaded file count and progress percentage.
- Once the download completes, the button is re-enabled for initiating another task.
Summary
This code provides a basic example of building a custom circular progress bar with functionalities like progress updates, text displays, and user interaction. You can further customize the design and functionalities based on your specific needs.
MainActivity.kt
package com.cfsuman.kotlintutorials
import android.app.Activity
import android.os.Bundle
import android.os.Handler
import android.os.Looper
import android.widget.Button
import android.widget.ProgressBar
import android.widget.TextView
import kotlin.random.Random
class MainActivity : Activity() {
var progressStatus = 0
var handler = Handler(Looper.getMainLooper())
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContentView(R.layout.activity_main)
// Get the widgets reference from XML layout
val button = findViewById<Button>(R.id.button)
val textView = findViewById<TextView>(R.id.textView)
val tvPercentage = findViewById<TextView>(R.id.tvPercentage)
val progressBar = findViewById<ProgressBar>(R.id.progressBar)
// button click listener
button.setOnClickListener {
button.isEnabled = false
// set up progress bar on initial stage
progressBar.progress = 0
progressStatus = 0
// generate random number of files to download
val filesToDownload= Random.nextInt(150,500)
// set up max value for progress bar
progressBar.max = filesToDownload
Thread {
while (progressStatus < filesToDownload) {
// update progress status
progressStatus += 1
// sleep the thread for 50 milliseconds
Thread.sleep(50)
// update the progress bar
handler.post {
progressBar.progress = progressStatus
// calculate the percentage
val percentage = ((progressStatus.toDouble()
/ filesToDownload) * 100).toInt()
// update the text view
textView.text = "Downloaded $progressStatus of " +
"$filesToDownload files"
tvPercentage.text = "$percentage%"
if (progressStatus == filesToDownload) {
button.isEnabled = true
}
}
}
}.start()
}
}
}
res/drawable/circular_progress_bar.xml
<?xml version="1.0" encoding="UTF-8"?>
<layer-list xmlns:android="http://schemas.android.com/apk/res/android" >
<item android:id="@android:id/background">
<shape android:shape="oval">
<stroke
android:color="#F4F0EC"
android:width="10dp"
android:dashWidth="5dp"
android:dashGap="5dp"/>
</shape>
</item>
<item android:id="@android:id/progress">
<rotate
android:fromDegrees="180"
android:toDegrees="180"
android:pivotX="50%"
android:pivotY="50%">
<shape
android:thickness="10dp"
android:shape="ring"
android:thicknessRatio="25.0"
android:innerRadiusRatio="2.2">
<gradient
android:startColor="#FF5722"
android:endColor="#00BCD4"
android:centerColor="#FFEB3B"
android:useLevel="true"
android:angle="90"
android:type="sweep"/>
</shape>
</rotate>
</item>
</layer-list>
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/rootLayout"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:padding="24dp"
android:background="#DCDCDC">
<TextView
android:id="@+id/textView"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginTop="24dp"
android:padding="12dp"
android:fontFamily="sans-serif"
android:textSize="20sp"
android:textStyle="bold"
tools:text="TextView"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toTopOf="parent" />
<TextView
android:id="@+id/tvPercentage"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:fontFamily="sans-serif"
android:padding="12dp"
android:textSize="20sp"
android:textStyle="bold"
app:layout_constraintBottom_toBottomOf="@+id/progressBar"
app:layout_constraintEnd_toEndOf="@+id/progressBar"
app:layout_constraintStart_toStartOf="@+id/progressBar"
app:layout_constraintTop_toTopOf="@+id/progressBar"
tools:text="Percentage" />
<ProgressBar
android:id="@+id/progressBar"
style="?android:attr/progressBarStyleHorizontal"
android:layout_width="250dp"
android:layout_height="250dp"
android:progressDrawable="@drawable/circular_progress_bar"
android:layout_marginTop="12dp"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toBottomOf="@+id/textView" />
<Button
android:id="@+id/button"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginTop="16dp"
android:text="Start Task"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toBottomOf="@+id/progressBar" />
</androidx.constraintlayout.widget.ConstraintLayout>