Android Kotlin: How to change an indeterminate progress bar color

This code demonstrates two methods for changing the color of an indeterminate progress bar in an Android application written in Kotlin. The code includes a button click listener that simulates a downloading process with a progress bar and text update.

Breakdown

The MainActivity.kt file handles the Activity logic:

  1. Setting Up References: It retrieves references to UI elements like buttons, text views, and progress bars from the layout file.
  2. Changing Indeterminate Progress Bar Color (Method 1):
    • Sets the indeterminateTintList property of a progress bar to a ColorStateList with a specific color (red in this case).
    • Sets the indeterminateTintMode property to PorterDuff.Mode.SRC_IN to apply the tint color.
  3. Changing Indeterminate Progress Bar Color (Method 2):
    • Checks the Android SDK version.
    • For API level 29 (Android Q) and above, it uses BlendModeColorFilter with the desired color (blue) and BlendMode.SRC_IN for tinting.
    • For versions below API 29, it uses the older setColorFilter method with the same color and PorterDuff.Mode.SRC_IN.
  4. Button Click Listener:
    • Simulates a download process:
      • Disables the button to prevent multiple clicks.
      • Shows all progress bars using TransitionManager for animation.
      • Resets progress and generates a random number of files to download.
      • Sets the maximum value of the progress bar based on the random number.
      • Starts a background thread to simulate the download progress.
    • The background thread:
      • Increments the progress counter.
      • Sleeps the thread for a short duration to simulate download time.
      • Updates the progress bar and text view on the main thread using Handler.post.
      • Calculates the download percentage.
      • Updates the text view with download status.
      • Once the download is complete (progress reaches the max value), it enables the button again and hides all progress bars with animation.

The activity_main.xml file defines the layout for the Activity:

  • It includes a ConstraintLayout to position the UI elements.
  • It has a TextView to display download status.
  • It defines four progress bars with indeterminate mode (they show animation without a defined progress).
  • It has a button to initiate the download process.

MainActivity.kt

package com.cfsuman.kotlintutorials

import android.app.Activity
import android.content.res.ColorStateList
import android.graphics.BlendMode
import android.graphics.BlendModeColorFilter
import android.graphics.Color
import android.graphics.PorterDuff
import android.os.Build
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)
        val progressBar2 = findViewById<ProgressBar>(R.id.progressBar2)
        val progressBar3 = findViewById<ProgressBar>(R.id.progressBar3)
        val progressBar4 = findViewById<ProgressBar>(R.id.progressBar4)


        // change indeterminate progressbar color (tint) programmatically
        progressBar3.indeterminateTintList = ColorStateList
            .valueOf(Color.RED)
        progressBar3.indeterminateTintMode = PorterDuff.Mode.SRC_IN


        // another way to change indeterminate progress bar color
        if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.Q) {
            progressBar4.indeterminateDrawable.colorFilter =
                BlendModeColorFilter(Color.BLUE, BlendMode.SRC_IN)
        }else {
            progressBar4.indeterminateDrawable
                .setColorFilter(Color.BLUE, PorterDuff.Mode.SRC_IN)
        }


        // button click listener
        button.setOnClickListener {
            button.isEnabled = false
            TransitionManager.beginDelayedTransition(rootLayout)
            progressBar.visibility = View.VISIBLE
            progressBar2.visibility = View.VISIBLE
            progressBar3.visibility = View.VISIBLE
            progressBar4.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(200,500)

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

            Thread(Runnable {
                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 ($percentage%)"

                        if (progressStatus == filesToDownload){
                            button.isEnabled = true
                            TransitionManager
                                .beginDelayedTransition(rootLayout)
                            progressBar.visibility = View.INVISIBLE
                            progressBar2.visibility = View.INVISIBLE
                            progressBar3.visibility = View.INVISIBLE
                            progressBar4.visibility = View.INVISIBLE
                        }
                    }
                }
            }).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:layout_marginTop="12dp"
        android:visibility="invisible"
        android:indeterminate="true"
        app:layout_constraintEnd_toEndOf="parent"
        app:layout_constraintStart_toStartOf="parent"
        app:layout_constraintTop_toBottomOf="@+id/textView" />

    <ProgressBar
        android:id="@+id/progressBar2"
        style="?android:attr/progressBarStyleHorizontal"
        android:layout_width="0dp"
        android:layout_height="wrap_content"
        android:layout_marginTop="12dp"
        android:visibility="invisible"
        android:indeterminate="true"
        android:indeterminateTint="#00A86B"
        android:indeterminateTintMode="src_in"
        app:layout_constraintEnd_toEndOf="parent"
        app:layout_constraintStart_toStartOf="parent"
        app:layout_constraintTop_toBottomOf="@+id/progressBar" />

    <ProgressBar
        android:id="@+id/progressBar3"
        style="?android:attr/progressBarStyleHorizontal"
        android:layout_width="0dp"
        android:layout_height="wrap_content"
        android:layout_marginTop="12dp"
        android:visibility="invisible"
        android:indeterminate="true"
        app:layout_constraintEnd_toEndOf="parent"
        app:layout_constraintStart_toStartOf="parent"
        app:layout_constraintTop_toBottomOf="@+id/progressBar2" />

    <ProgressBar
        android:id="@+id/progressBar4"
        style="?android:attr/progressBarStyleHorizontal"
        android:layout_width="0dp"
        android:layout_height="wrap_content"
        android:layout_marginTop="12dp"
        android:visibility="invisible"
        android:indeterminate="true"
        app:layout_constraintEnd_toEndOf="parent"
        app:layout_constraintStart_toStartOf="parent"
        app:layout_constraintTop_toBottomOf="@+id/progressBar3" />

    <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/progressBar4" />

</androidx.constraintlayout.widget.ConstraintLayout>