Android Kotlin: Programmatically Creating a Horizontal Progress Bar
This code demonstrates how to create and manage a horizontal progress bar programmatically in an Android app written with Kotlin. It achieves this by inflating a basic layout containing a Button and a TextView, then dynamically adding and configuring a progress bar within the Activity code.
The code utilizes ConstraintLayout to manage the positioning of the progress bar relative to the existing UI elements. It also simulates a download process by updating the progress bar and TextView on a separate thread.
Breakdown and Summary
The code is divided into two main parts: the activity_main.xml
layout file and the MainActivity.kt
class.
activity_main.xml: This file defines a simple layout with a Button and a TextView.
MainActivity.kt: This class handles the core functionality of the app. Here's a breakdown of the key steps:
- Variable Initialization: It initializes variables for progress status, a handler for UI updates, and references to UI elements obtained from the layout.
- Progress Bar Creation: A horizontal progress bar is programmatically created, assigned a unique ID, sized with
MATCH_PARENT
width andWRAP_CONTENT
height, and added to the ConstraintLayout. - Constraint Layout Configuration: A ConstraintSet object is used to define the positioning of the progress bar relative to the TextView. It's placed below the TextView with a margin and centered horizontally within the layout.
- Button Click Listener: The Button click listener is set. Clicking the button initiates the download simulation:
- Disables the button to prevent multiple clicks.
- Resets progress and progress status.
- Generates a random number of files to download (simulating download size).
- Sets the maximum value of the progress bar based on the generated file count.
- Starts a separate thread to simulate the download process:
- The thread iteratively increments the progress status.
- The UI thread's handler is used to update the progress bar value, TextView text (showing downloaded files and percentage), and finally re-enables the button when the download is complete.
This code demonstrates a practical approach to creating and managing a progress bar for download simulations or any other operations requiring visual progress indication within an Android app.
package com.cfsuman.kotlintutorials
import android.app.Activity
import android.content.Context
import android.os.Bundle
import android.os.Handler
import android.os.Looper
import android.util.TypedValue
import android.view.View
import android.widget.Button
import android.widget.ProgressBar
import android.widget.TextView
import androidx.constraintlayout.widget.ConstraintLayout
import androidx.constraintlayout.widget.ConstraintLayout.LayoutParams
import androidx.constraintlayout.widget.ConstraintSet
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 references from XML layout
val rootLayout = findViewById<ConstraintLayout>(R.id.rootLayout)
val button = findViewById<Button>(R.id.button)
val textView = findViewById<TextView>(R.id.textView)
// Create a horizontal progress bar programmatically
val progressBar = ProgressBar(
this,
null,
android.R.attr.progressBarStyleHorizontal
)
// Generate a view id for the progress bar
progressBar.id = View.generateViewId()
// Progress bar width and height
val params = LayoutParams(
LayoutParams.MATCH_PARENT,
LayoutParams.WRAP_CONTENT
)
progressBar.layoutParams = params
// Add the progress bar to constraint layout
rootLayout.addView(progressBar)
// Initialize a new constraint set
val constraintSet = ConstraintSet()
constraintSet.clone(rootLayout)
// Put the progress bar bottom of text view
constraintSet.connect(
// Connect progress bar top to text view's bottom
progressBar.id,
ConstraintSet.TOP,
R.id.textView,
ConstraintSet.BOTTOM,
24.toDp(this) // Margin
)
// Start constraint with margin
constraintSet.connect(
progressBar.id,
ConstraintSet.START,
R.id.rootLayout,
ConstraintSet.START,
16.toDp(this) // Margin
)
// End constraint with margin
constraintSet.connect(
progressBar.id,
ConstraintSet.END,
R.id.rootLayout,
ConstraintSet.END,
16.toDp(this)
)
// Finally, apply the constraint to constraint layout
constraintSet.applyTo(rootLayout)
// Set the 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(10,200)
// Set up max value for progress bar
progressBar.max = filesToDownload
Thread {
while (progressStatus < filesToDownload) {
// Update progress status
progressStatus += 1
// Sleep the thread for 100 milliseconds
Thread.sleep(100)
// Update the progress bar
handler.post {
progressBar.progress = progressStatus
// Calculate the percentage
var percentage = ((progressStatus.toDouble()
/ filesToDownload) * 100).toInt()
// Update the text view
textView.text = "Downloaded $progressStatus of " +
"$filesToDownload files ($percentage%)"
if (progressStatus == filesToDownload) {
button.isEnabled = true
}
}
}
}.start()
}
}
}
// Extension method to convert values to dp
fun Int.toDp(context: Context):Int = TypedValue.applyDimension(
TypedValue.COMPLEX_UNIT_DIP,
this.toFloat(),
context.resources.displayMetrics
).toInt()
<?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">
<Button
android:id="@+id/button"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="Start Task"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintHorizontal_bias="0.498"
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="24dp"
android:fontFamily="sans-serif"
android:textSize="20sp"
android:textStyle="bold"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toBottomOf="@+id/button"
tools:text="TextView" />
</androidx.constraintlayout.widget.ConstraintLayout>