Introduction
This code demonstrates creating a custom horizontal progress bar in an Android application written in Kotlin. The progress bar simulates downloading a random number of files. The code includes three parts:
- The
MainActivity.kt
file handles the activity logic, user interaction, and progress updates. - The
progressbar_states.xml
file defines the custom drawable used for the progress bar. - The
activity_main.xml
file defines the user interface layout for the activity.
Breakdown of MainActivity.kt
The MainActivity.kt
file manages the core functionality of the application. Here's a breakdown of the code:
- Variable Initialization: It declares variables for
progressStatus
to track download progress, and ahandler
to update the UI thread. - onCreate(): This function sets up the activity when it's first created. Here, it:
- Inflates the layout defined in
activity_main.xml
. - Finds references to the button, text view, and progress bar using their IDs.
- Inflates the layout defined in
- Button Click Listener: This code defines what happens when the user clicks the button:
- Disables the button to prevent multiple clicks.
- Resets progress variables and the progress bar.
- Generates a random number of files to download (between 10 and 200).
- Sets the maximum value of the progress bar based on the number of files.
- Creates a new thread to simulate the download process.
Functionality within the Thread
The code within the new thread continuously loops until all files are downloaded:
- It increments the
progressStatus
variable to simulate file download. - The thread sleeps for 200 milliseconds to simulate download time.
- It uses the
handler.post
method to update the UI thread safely. Inside the handler:- The progress bar value is updated based on the current progress.
- The percentage downloaded is calculated and displayed in the text view.
- The button is re-enabled once all files are downloaded.
Summary
This code showcases a custom horizontal progress bar implementation in Kotlin. It demonstrates how to use threads, handlers, and UI updates to simulate a download process and provide visual feedback to the user. The custom drawable defined in progressbar_states.xml
creates the unique visual style of the progress bar.
package com.example.jetpack
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 androidx.appcompat.app.AppCompatActivity
import kotlin.random.Random
class MainActivity : AppCompatActivity() {
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 progressBar = findViewById<ProgressBar>(R.id.progressBar)
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(Runnable {
while (progressStatus < filesToDownload){
// update progress status
progressStatus +=1
// sleep the thread for 200 milliseconds
Thread.sleep(200)
// 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()
}
}
}
<?xml version="1.0" encoding="utf-8"?>
<layer-list xmlns:android="http://schemas.android.com/apk/res/android">
<item android:id="@+id/background">
<shape android:shape="line">
<stroke
android:width="3dp"
android:dashWidth="20dp"
android:dashGap="7dp"
android:color="#FFEB3B"
/>
</shape>
</item>
<item android:id="@+id/progress">
<clip android:clipOrientation="horizontal" android:gravity="start">
<shape android:shape="line">
<stroke
android:width="3dp"
android:dashWidth="20dp"
android:dashGap="7dp"
android:color="#2196F3"
/>
</shape>
</clip>
</item>
</layer-list>
<?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"
tools:context=".MainActivity">
<com.google.android.material.textview.MaterialTextView
android:id="@+id/textView"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginStart="16dp"
android:layout_marginTop="8dp"
android:padding="24dp"
android:text="Stats"
android:textAppearance="@style/TextAppearance.MaterialComponents.Headline6"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toTopOf="parent" />
<com.google.android.material.button.MaterialButton
android:id="@+id/button"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginTop="8dp"
android:text="Start Task"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toBottomOf="@+id/progressBar" />
<ProgressBar
android:id="@+id/progressBar"
style="?android:attr/progressBarStyleHorizontal"
android:layout_width="0dp"
android:layout_height="wrap_content"
android:layout_marginStart="8dp"
android:layout_marginTop="12dp"
android:layout_marginEnd="8dp"
android:progressDrawable="@drawable/progressbar_states"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintHorizontal_bias="0.498"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toBottomOf="@+id/textView" />
</androidx.constraintlayout.widget.ConstraintLayout>