Android ProgressBar widget

In this chapter of the Android development tutorial we will present the ProgressBar widget. A ProgressBar is a widget that shows visually a progress of some task. The widget comes in two basic modes. There is a circular bar and a horizontal bar.

We will have two examples to demonstrate both of them.

ProgressBar I

We have a horizontal ProgressBar widget and a TextView widget that shows the percentage of the task completed. The manifest file is left untouched.

main.xml
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
    android:orientation="vertical"
    android:layout_width="fill_parent"
    android:layout_height="fill_parent"
    >
<ProgressBar 
    android:id="@+id/pbId"
    android:layout_width="fill_parent"
    android:layout_height="wrap_content"
    style="?android:attr/progressBarStyleHorizontal"
    android:layout_margin="10dp"
    />       
<TextView
    android:id="@+id/tvId"
    android:layout_width="fill_parent"
    android:layout_height="wrap_content"
    android:layout_margin="10dp"
    />
</LinearLayout>

In the main.xml layout file, we have a ProgressBar and a TextView. The style="?android:attr/progressBarStyleHorizontal" style makes the ProgressBar horizontal. The default mode of the ProgressBar is the circular mode.

strings.xml
<?xml version="1.0" encoding="utf-8"?>
<resources>
    <string name="app_name">ProgBar</string>
</resources>

String resource file.

MainActivity.java
package com.zetcode.progbar2;

import android.app.Activity;
import android.os.Bundle;
import android.os.Handler;
import android.os.Message;
import android.widget.ProgressBar;
import android.widget.TextView;
import android.util.Log;


public class MainActivity extends Activity
{
    ProgressBar pb;
    TextView tv;
    int prg = 0;

    @Override
    public void onCreate(Bundle savedInstanceState)
    {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.main);

        pb = (ProgressBar) findViewById(R.id.pbId);
        tv = (TextView) findViewById(R.id.tvId);
 
        new Thread(myThread).start();
    }

    private Runnable myThread = new Runnable()
    { 
        @Override
        public void run() 
        {
            while (prg < 100)
            {
                try
                {
                    hnd.sendMessage(hnd.obtainMessage());
                    Thread.sleep(100);
                }
                catch(InterruptedException e) 
                {  
                    Log.e("ERROR", "Thread was Interrupted");
                }
            }

            runOnUiThread(new Runnable() { 
                public void run() {
                    tv.setText("Finished");
                }
            });          
        }
    
        Handler hnd = new Handler()
        {    
            @Override
            public void handleMessage(Message msg) 
            {
                prg++;
                pb.setProgress(prg);

                String perc = String.valueOf(prg).toString();
                tv.setText(perc+"% completed");
            }
        };
    };
}

We create a thread to control the progress of a ProgressBar.

new Thread(myThread).start();

A new thread is started. In Android, lengthy tasks should by performed inside a thread to prevent the application from appearing unresponsive. A thread ends by returning from its main() method, or by an exception.

@Override
public void run() 
{
    while (prg < 100)
    {
        try
        {
            hnd.sendMessage(hnd.obtainMessage());
            Thread.sleep(100);
        }
        catch(InterruptedException e) 
        {  
            Log.e("ERROR", "Thread was Interrupted");
        }
    }

    runOnUiThread(new Runnable() { 
        public void run() {
            tv.setText("Finished");
        }
    });          
}

The code in a thread is placed in the run() method. We will simulate a lengthy task by calling the Thread.sleep() method. This forces us to handle the InterruptedException. Android application runs in a single-thread model. All components of the main activity are created in the main thread. These components cannot be manipulated in other threads. To work around this, we use either the Handler object or call the runOnUiThread() method.

runOnUiThread(new Runnable() { 
    public void run() {
        tv.setText("Finished");
    }
});   

Only the original thread that created a view hierarchy can touch its views. Here we are modifying the TextView widget. Therefore we have put the code into the runOnUiThread() method, which runs the code in the main, UI thread, where the widget was created.

Handler hnd = new Handler()
{    
    @Override
    public void handleMessage(Message msg) 
    {
        prg++;
        pb.setProgress(prg);

        String perc = String.valueOf(prg).toString();
        tv.setText(perc+"% completed");
    }
};

Another way to touch widgets from another thread is to use the Handler object. It is used to enqueue an action to be performed on a different thread than its own. We update the progress bar and set a percentage of the task completed to the text view.

ProgressBar widget
Figure: ProgressBar widget

ProgressBar II

In the second example, we show the usage of the ProgressBar in a circular mode. The manifest file does not need to be modified.

main.xml
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
    android:orientation="vertical"
    android:layout_width="fill_parent"
    android:layout_height="fill_parent"
    >
<ProgressBar 
    android:id="@+id/pbId"
    android:layout_width="wrap_content"
    android:layout_height="wrap_content"
    />    
<TextView
    android:id="@+id/tvId"
    android:layout_width="fill_parent"
    android:layout_height="wrap_content"
    android:text="@string/msg"
    />        
</LinearLayout>

In the main.xml file we have a ProgressBar and a TextView. The ProgressBar has the default style, which is the circular style. This is the same as if we have used the style="?android:attr/progressBarStyle" attribute.

strings.xml
<?xml version="1.0" encoding="utf-8"?>
<resources>
    <string name="app_name">CirProgBar</string>
    <string name="msg">Please wait...</string>
</resources>

We have two string resources in the strings.xml file.

MainActivity.java
package com.zetcode.progbar;

import android.app.Activity;
import android.os.Bundle;
import android.os.Handler;
import android.os.Message;
import android.widget.ProgressBar;
import android.widget.TextView;
import android.view.View;
import android.util.Log;

public class MainActivity extends Activity
{
    ProgressBar pb;
    TextView tv;
    int prg = 0;

    @Override
    public void onCreate(Bundle savedInstanceState)
    {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.main);

        pb = (ProgressBar) findViewById(R.id.pbId);
        tv = (TextView) findViewById(R.id.tvId);
 
        new Thread(myThread).start();
    }

    private Runnable myThread = new Runnable()
    { 
        @Override
        public void run() 
        {
            while (prg < 100)
            {
                try
                {
                    hnd.sendMessage(hnd.obtainMessage());
                    Thread.sleep(100);
                }
                catch(InterruptedException e) 
                {  
                    Log.e("ERROR", "Thread was Interrupted");
                }
            }

            runOnUiThread(new Runnable() { 
                public void run() {
                    tv.setText("Finished");
                    pb.setVisibility(View.GONE); 
                }
            });          
        }
    
        Handler hnd = new Handler()
        {    
            @Override
            public void handleMessage(Message msg) 
            {
                prg++;
                pb.setProgress(prg);
            }
        };
    };
}

The code is similar to the first example with a few modifications.

runOnUiThread(new Runnable() { 
    public void run() {
        tv.setText("Finished");
        pb.setVisibility(View.GONE); 
    }
}); 

After the task was completed, we hide the ProgressBar using the setVisibility() method. The circle itself is an endless animation, so after the task was finished, we need to hide the widget.

Circular ProgressBar widget
Figure: Circular ProgressBar widget

In this chapter of the Android development tutorial, we have mentioned ProgressBar widget.