Android Kotlin: How to create an indeterminate progress bar

This code demonstrates how to create an indeterminate progress bar in an Android app written in Kotlin. An indeterminate progress bar indicates an ongoing operation without a specific duration. It's useful for scenarios where the exact time to complete a task is unknown.

Breakdown of the code:

The code consists of two parts:

  1. MainActivity.kt: This file handles the logic behind the progress bar functionality.
  2. activity_main.xml: This file defines the user interface layout for the app.

MainActivity.kt:

  • It defines variables for tracking progress (progressStatus) and a handler (handler) for updating the UI thread.
  • In the onCreate method, it references the UI elements (button, text view, progress bar) from the layout.
  • The button click listener performs the following actions:
    • Disables the button to prevent multiple clicks.
    • Uses TransitionManager for a smooth animation when showing the progress bar.
    • Sets the progress bar properties: initial progress, max value, and visibility.
    • Starts a new thread to simulate a download process.
  • The thread continuously updates the progressStatus and sleeps for a short duration to mimic download time.
  • Inside the thread, a handler.post block updates the UI thread:
    • Calculates the download percentage.
    • Updates the text view with download progress.
    • Once the download is complete, it re-enables the button and hides the progress bar with another animation.

activity_main.xml:

  • This file defines the layout using ConstraintLayout.
  • It includes a text view displaying progress information, a progress bar with specific attributes, and a button to trigger the download task.

Summary

This code provides a basic example of using an indeterminate progress bar in Kotlin. It demonstrates how to simulate a download process with a progress indicator, update the UI thread from a background thread, and manage button interaction during the download. You can adapt this code for various scenarios where you need to visually represent an ongoing operation in your Android app.


MainActivity.kt

package com.cfsuman.kotlintutorials

import android.app.Activity
import android.os.Bundle
import android.os.Handler
import android.os.Looper
import android.view.View
import android.widget.Button
import android.widget.ProgressBar
import android.widget.TextView
import androidx.constraintlayout.widget.ConstraintLayout
import androidx.transition.TransitionManager
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 rootLayout = findViewById<ConstraintLayout>(R.id.rootLayout)
        val button = findViewById<Button>(R.id.button)
        val textView = findViewById<TextView>(R.id.textView)
        val progressBar = findViewById<ProgressBar>(R.id.progressBar)


        // Button click listener
        button.setOnClickListener {
            button.isEnabled = false
            TransitionManager.beginDelayedTransition(rootLayout)
            progressBar.visibility = View.VISIBLE

            // Set up progress bar on initial stage
            progressBar.progress = 0
            progressStatus = 0

            // Generate random number of files to download
            val filesToDownload= Random.nextInt(10,50)

            // Set up max value for progress bar
            progressBar.max = filesToDownload

            Thread {
                while (progressStatus < filesToDownload) {
                    // Update progress status
                    progressStatus += 1

                    // Sleep the thread for 200 milliseconds
                    Thread.sleep(200)

                    // Update the progress bar
                    handler.post {
                        // 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
                            TransitionManager
                                .beginDelayedTransition(rootLayout)
                            progressBar.visibility = View.GONE
                        }
                    }
                }
            }.start()
        }
    }
}
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" />

    <ProgressBar
        android:id="@+id/progressBar"
        style="?android:attr/progressBarStyleHorizontal"
        android:layout_width="0dp"
        android:layout_height="wrap_content"
        android:visibility="gone"
        android:indeterminate="true"
        android:indeterminateBehavior="cycle"
        android:indeterminateOnly="true"
        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>