Progress Bars

When we must
wait for some task to complete in a modern graphical user interface, the interface normally provides visual feedback that something is happening, often with an indication of how much progress has been made on the task. The widgets providing this information
are commonly termed progress bars (though they need not be bars), and are essential to providing a sense of application responsiveness for the user.
An overview of design philosophy concerning progress bars may be found in the
Progress and Activity document.
Implementing a progress bar also provides an opportunity to illustrate two other important Android concepts:

running processes on threads separate from the main user interface (UI) and

communicating between threads.

In this example we shall demonstrate how to implement progress bars and to update them from processes running on a separate thread.

Progress Bars

A progress bar is an instance of the Android class
ProgressBar.
There are two basic types of progress bars that we can implement:

Indeterminate Progress Bar: A spinning or otherwise continuously moving symbol that indicates progress but of an unspecified amount. This is typically used when we wish to indicate to the user that something is happening behind the scene, but we don't know, or don't wish to display, how long it will be happening.

Determinate Progress Bar: A horizontal bar or some other shape that fills to show a quantitative amount of progress. This is typically employed when we wish to indicate to the user how much of a task happening behind the scene (for example, download of a large image file) has been completed.

Traditionally, progress bars have been displayed in a
Dialog window floating above the main window, with the dialog window that holds the progress bar closing when the underlying main window is ready to display the content on which we have been waiting. A more modern approach adopts a minimalist philosophy and discards floating dialogs, instead embedding the progress bar directly in the window that will hold the new content, with the new content then replacing the progress bar in that window when it is ready to display.

In this project we shall illustrate how to implement both determinate and indeterminate progress bars, in both floating dialogs and embedded inline in the main window. We shall also introduce the rudiments of implementing a custom progress bar,
and illustrate ways to update a progress bar from
a thread that is separate from the main UI thread.

The style and preferred method of implementing progress bars has changed substantially since the early days of
Android. We shall emphasize newer devices here and set a minimal SDK of API level 13 (Android 3.2) on this project. If you need to develop for devices running versions of Android earlier than this, Android compatibility libraries may be used to retrofit the current project to run on older devices, or you may download a
deprecated version of the current project designed for versions earlier than 3.1.
This version uses style common before the advent of the Holo theme and uses methods to implement both dialog boxes and progress bars that were deprecated as of Android 3.1. This deprecated version will still function on newer devices, but it uses methods that are deprecated and thus not guaranteed to function in future Android releases. You should not use the deprecated version unless you are developing only for older devices.

Threads

Threads are instances of the
Thread
class that implement concurrent units of execution in your code. A thread has its own call stack for methods being invoked, their arguments and local variables. When an app starts, it launches in its own virtual machine. Each such virtual machine instance has at least one main thread running when it is started, there typically will be others invoked by the system for housekeeping, and the application may create additional threads for specific purposes. A common use of threads in Android applications is to move time-consuming and potentially blocking operations such as computationally-intensive loops and network operations off the main UI thread so that they do not compromise responsiveness of the user interface.

There are two standard ways of implementing threads in Android that are based largely on the Java thread model:

Create a new class that extends
Thread
and override (provide your own implementation of) its
run()
method.

Provide a new
Thread
instance with a
Runnable
object during its creation using the Runnable interface. When the Runnable interface is invoked, you must provide an implementation of its
run()
method. This method will be called whenever a thread is started that was created with a class implementing the Runnable interface.

In either case, the resulting thread is not executed until its
start() method is invoked.
We shall give an example of the second approach in the progress bar examples
below and an example of the first approach in the project
Animator Demo.

In this project we shall implement Threads directly, but for simple threading tasks the Android class
AsyncTask may be easier to use because it abstracts much of the thread management. Specifically, AsyncTask is a helper class supplementing Thread and Handler that can perform operations on a background thread and publish results on the UI thread without the programmer having to manipulate directly either threads or handlers. We illustrate the use of AsyncTask in various other projects. For example, see the
Web Data Streams I project.

Communication Between Threads

As we have seen,
to facilitate UI responsiveness it is often desirable to offload tasks to new threads that the app spawns.
Usually this means that at some stage the main UI thread and the new thread must communicate, but there are some strict rules about communication between threads that must be respected.
The issue is particularly acute if the task on the new thread wishes to modify views associated with main UI thread, because it is strictly forbidden to do so directly.

In particular, a common task in animation programming is to modify the parameters defining some graphics on the screen implemented through a View within a repetitive loop (with some delay to control speed of change), and then to call the invalidate() method of View to request that the View redraw itself as soon as possible each time through the loop. If the graphics parameters for a View defined on the main UI thread are changed in a loop running on a separate thread, the non-UI thread is forbidden to call invalidate() on the View because it is on a different thread. Since the View cannot change unless it is redrawn, we must circumvent this restriction if we are to animate something from a separate thread. There are three common ways to do this.

Instead of invoking the View method
invalidate( ),
which can be called only from the UI thread, invoke the View method
postInvalidate( ),
which can be called from a non-UI thread. Whereas invalidate( ) requests a redraw as soon as possible,
postInvalidate( ) causes a redraw only during a subsequent iteration of the loop, which is why it is safe to invoke it from a separate thread.

The second thread cannot touch a View on the main UI thread, but we can
define a Handler on the main UI thread (typically by defining an inner class---inside the class defining the View---that subclasses Handler), send messages with data to it from the second thread, and use those messages to cause the Handler to invalidate the View on its own (UI) thread.

A Handler sends and processes
Message and
Runnable objects associated with a thread's
MessageQueue.
When a new Handler is created, it is bound to the thread that creates it and its message queue; subsequently, it will deliver messages and runnables to that message queue and execute them as they come out of the message queue.
There are two primary uses for Handler:

Schedule messages and runnables for future execution.

Queue actions to be performed on a different thread.

In the present example it is this second function of Handler that we shall employ.

The preceding two approaches define redraws on a Canvas supplied by the View through its
onDraw method (which we override to define our drawing tasks). An alternative approach is to draw using
a
SurfaceView with a
SurfaceHolder interface defined on a dedicated thread. The advantage of this approach is that it generally can handle faster animation because graphical changes can be rendered to the screen as fast as the thread is running instead of requesting a screen redraw using invalidate( ) or postInvalidate( ) that will happen on a timescale controlled by Android. The disadvantage is that
in this case the programmer must obtain and manage the drawing Canvas, so its implementation is somewhat more complex.

We shall discuss these alternative approaches in more depth in the
Animator Demo project.
In the present example we shall use the second approach (a Handler), and in subsequent projects we shall illustrate use of the other two approaches.

Creating the Project

Create a new project in Eclipse by selecting New > Android Application Project
(or File > Project > Android > Android Application Project > Next) and filling in the following information
on the screen that results.

Click Next and then accept the defaults on the remaining screens to create the project ProgressBarExample.

The Code

We are now ready to create the files and insert the code to construct our project. We first add the XML files and then the Java files. There will be a number of XML files because we are going to illustrate several ways to display progress bars.

XML Files

First we define some strings and set the entry-screen layout.
Edit res/values/strings.xml to read

Now we need to specify some XML that will define some custom themes and styles. Create the file res/drawable-hdpi/custom_progress_bar.xml. (Note: this is in the directory res/drawable-hdpi; create it by right-clicking on res/drawable-hdpi and select New > Android XML File.) Edit this file to give

(where <YourNamespace> should be changed to your namespace). Create the class file src/<YourNamespace>.progressbarexample/DialogFrag.java and edit it to read

package <YourNamespace>.progressbarexample;
import android.app.DialogFragment;
import android.app.ProgressDialog;
import android.os.Bundle;
import android.os.Handler;
import android.os.Message;
import android.util.Log;
import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;
import android.widget.ProgressBar;
import android.content.Context;
import android.content.Intent;
public class DialogFrag extends DialogFragment {
private static final String TAG = "PROGRESS";
ProgressDialog progDialog;
ProgressBar pbar;
public static Context context;
int barType;
View v;
private ProgressThread progThread;
private static final int delay = 20; // Milliseconds delay in update loop
private int maxBarValue; // Max value horizontal progress bar
boolean threadStopped = false;
// Whether to implement holo dark (if false) or holo light (if true) theme for
// dialog window that holds the progress bar.
boolean lightTheme = true;
// Public empty constructor. Required for subclasses of Fragment; see
// http://developer.android.com/reference/android/app/Fragment.html
public DialogFrag() {
}
// Method to create new instance of DialogFrag, passing the integer num
// as an argument. See the example at
//
// http://developer.android.com/reference/android/app/DialogFragment.html
//
// This is one way to avoid defining a non-default constructor to pass arguments
// to a Fragment, which would not be good for performance. Passing arguments
// to a Fragment can also be accomplished using the default constructor and the
// setArguments(Bundle, args) method of Fragment. See
//
// http://developer.android.com/reference/android/app/Fragment.html#setArguments(android.os.Bundle)
//
// for further discussion. (It can only be called before the Fragment has been attached to an
// activity, so setArguments() should be called immediately after constructing the Fragment.)
static DialogFrag newInstance(int num) {
DialogFrag f = new DialogFrag();
// Supply num input as an argument.
Bundle args = new Bundle();
args.putInt("num", num);
f.setArguments(args);
return f;
}
public void onCreate(Bundle savedInstanceState){
super.onCreate(savedInstanceState);
barType = getArguments().getInt("num");
// Set styles and themes for the DialogFragment window holding the ProgressBar
// (custom styles and themes for the progress bar itself are set in res/values/styles.xml)
int style = DialogFragment.STYLE_NO_TITLE;
int theme = android.R.style.Theme_Holo_Dialog;
if(lightTheme){
theme = android.R.style.Theme_Holo_Light_Dialog;
}
this.setStyle(style, theme);
}
/** This (optional) callback is executed when the fragment is ready to instantiate its
* user interface (if it has one). The variable inflater is the LayoutInflater that will
* inflate any views in the fragment, the variable container is the parent view that the
* fragment UI should be attached to (the fragment does not add the view itself, but container
* can be used to generate the LayoutParams for the view). If savedInstanceState is not null,
* the fragment is being reconstructed from a previous saved state. This callback is executed between
* onCreate() and onActivityCreated() and returns the View for the fragment's UI, or null if
* there is no UI. If a View is returned by this method, the onDestroyView() callback will be
* called when the View is being released.
*/
@Override
public View onCreateView(LayoutInflater inflater, ViewGroup container,
Bundle savedInstanceState) {
// Inflate the views and get handles to its elements here. We cannot use
// findViewById in onCreate() in the usual way because this is a Fragment,
// not an Activity, and only when this callback fires (which occurs after onCreate() is executed)
// is the Fragment ready to lay out its view.
if(barType == 0){
v = inflater.inflate(R.layout.progbar_only, container, false);
pbar = (ProgressBar) v.findViewById(R.id.progress_bar_only);
} else {
v = inflater.inflate(R.layout.progbar_horiz_only, container, false);
pbar = (ProgressBar) v.findViewById(R.id.progress_bar_horiz_only);
}
// Set the max value of the progress bar
maxBarValue = pbar.getMax();
// Start the background thread
progThread = new ProgressThread(handler);
progThread.start();
return v;
}
// Callback executed when fragment View is being released. Not doing anything
// with it now.
@Override
public void onDestroy(){
super.onDestroy();
}
// Method to close dialog fragment window. Need the check on threadStopped
// because this method can be called twice before the thread gets
// stopped and if called a second time an exception will be thrown since
// the fragment was already dismissed the first time it was called.
private void closeDialog(){
if(!threadStopped){
Intent i = new Intent(context, DisplayMessage.class);
startActivity(i);
if(!this.isDetached()) dismiss();
}
}
/**
* Handler on the main (UI) thread that will receive messages from the
* second thread and update the progress. Note that Eclipse warns that this
* class should be declared static to prevent a possible memory leak
* (because as an inner class it could prevent garbage collection for the
* outer enclosing class). However, it seems from the explanation that there is
* no problem if (as is true here) the Handler is dealing with messages from a
* thread other than the main thread.
*/
final Handler handler = new Handler() {
public void handleMessage(Message msg) {
// Get the current value of the variable total from the message data
// and update the progress bar.
int total = msg.getData().getInt("total");
pbar.setProgress(total);
if (total > maxBarValue){
// Stop the background thread
progThread.setState(ProgressThread.DONE);
// Hide the dialog window contents and close it
pbar.setVisibility(ProgressBar.INVISIBLE);
v.setVisibility(View.INVISIBLE);
closeDialog();
threadStopped = true;
}
}
};
// Inner class that performs progress calculations on a second thread. Implement
// the thread by subclassing Thread and overriding its run() method. Also provide
// a setState(state) method to stop the thread gracefully. This can be done
// more compactly, as illustrated in the class ProgressExample, and could also be
// done with AsyncTask rather than the explicit Java threads we have used here.
private class ProgressThread extends Thread {
// Class constants defining state of the thread
final static int DONE = 0;
final static int RUNNING = 1;
Handler mHandler;
int mState;
int total;
// Constructor with an argument that specifies Handler on main thread
// to which messages will be sent by this thread.
ProgressThread(Handler h) {
mHandler = h;
}
// Override the run() method that will be invoked automatically when
// the Thread starts. Do the work required to update the progress bar on this
// thread but send a message to the Handler on the main UI thread to actually
// change the visual representation of the progress.
@Override
public void run() {
mState = RUNNING;
total = 0;
threadStopped = false;
while (mState == RUNNING) {
// The method Thread.sleep throws an InterruptedException if Thread.interrupt()
// were to be issued while thread is sleeping; the exception must be caught.
try {
// Control speed of update (but precision of delay not guaranteed)
Thread.sleep(delay);
} catch (InterruptedException e) {
Log.e("ERROR", "Thread was Interrupted");
}
// Send message (with current value of total as data) to Handler on UI thread
// so that it can update the progress bar.
Message msg = mHandler.obtainMessage();
Bundle b = new Bundle();
b.putInt("total", total);
msg.setData(b);
mHandler.sendMessage(msg);
total++;
}
}
// Set current state of thread (use value state=ProgressThread.DONE to stop thread)
public void setState(int state) {
mState = state;
}
}
}

Create the file src/<YourNamespace>.progressbarexample/DisplayMessage.java and edit it to read

This completes entry of the code and the project should now compile without errors.

Trying it Out

If you execute this project on a device or emulator, you should see an initial display with five buttons. If the first button is pressed you should see a display like the left figure below, and you should see a display like the right figure below if the second button is pressed.

In the left figure above an indeterminate progress bar is displayed as a small rotating circle (often termed a spinner). In the right figure above a determinate progress bar is displayed as a continuously increasing blue bar (in the Holo Light theme).

Best practices in Android design emphasize conveying information graphically where possible, with minimal text. In the above example for the indeterminate spinner we have shown how to display a text message associated with the spinner. Generally, you should not display such text unless it conveys additional important information. If you implement consistent progress bar design, a user of your app should quickly become adapted to interpreting the spinner graphic correctly without having to add clarifying text. One example of where text might be useful is if the background operation is long but indeterminate in length. Then it may be a user-friendly gesture to display a message to the effect that "this may take a while" so that the user knows that something useful is happening and your code has not just gotten into an infinite loop. But generally, eschew text on progress bars (and other iconic graphics) unless it conveys essential additional information.

Notice also that in the above spinner graphic example the spinner is hard to see because it is of light color displayed against the light background. In this case a different choice for the style making either the spinner or the background darker might be useful. For example, in DialogFrag.java the boolean variable lightTheme can be set to false to produce a dark background for the spinner.

In the preceding images the progress bar is displayed in a dialog window floating above the main display, with the display dimmed in the background. In the following two figures we illustrate a more modern variant in which the progress bar is displayed inline rather than in a floating dialog.

The left figure shows a variant of the indeterminate spinner where a pulsing horizontal line is displayed rather than a rotating circle when the third button is pressed. As soon as the thread returns, the progress bar disappears and is replaced inline by the text field that we were waiting to display (right figure above).

Pressing the fourth button gives the progress bar displayed inline in the figure below left, which is a determinate horizontal bar displayed inline in Holo Light theme. Pressing the fifth button gives the determinate progress bar displayed inline in the figure below right, which illustrates defining a custom style for a progress bar.

In both cases the progress bar is displayed inline rather than in a floating dialog window and when the progress bar disappears it is replaced immediately by the content to be loaded in the same window, as was illustrated above for the inline indeterminate progress bar.

How It Works

The comments in the files MainActivity.java, ProgressExample.java and DialogFrag.java outline the functionality but we give a somewhat more expansive description here.

The files activity_main.xml and strings.xml are used to lay out an initial screen with five buttons in a manner that should be familiar from earlier examples.
We then use findViewById in MainActivity.java to identify the buttons and attach clickListeners to them, with code to process the button defined in anonymous inner classes.

The first two buttons illustrate placing progress bars in floating dialog windows by creating instances of the class DialogFrag, which subclasses
DialogFragment.

The class
DialogFragment
extends
Fragment,
which is a piece of an application's user interface or behavior that can be placed in an Activity. A DialogFragment is
a Fragment that displays a dialog window, floating on top of its activity's window. The Fragment in the present application contains a
Dialog object. (However, one should not make direct calls on this Dialog but should instead control it through the API described at
DialogFragment.)

The bottom three buttons launch instances of the class ProgressExample, which creates progress bars inline rather than in a floating dialog window.

Let us now discuss in more detail the classes defined by DialogFrag.java and ProgressExample.java, and how they implement these various progress bars.

The Class DialogFrag

The class DialogFrag creates a dialog window that is a Fragment, which in this application will hold a ProgressBar.
Because DialogFrag is a subclass of Fragment, it has to be treated somewhat differently than an Activity. Notice first that because of the way the fragment functions (it is possibly instantiated and reinstantiated multiple times), any subclass of Fragment must have a default empty (no arguments) constructor, and for performance reasons it is discouraged to define additional (overloaded) constructors that pass arguments. Thus, we cannot pass arguments to a fragment through its constructor. The code segment

illustrates one way to create an instance of DialogFrag and pass arguments to it. In this example an instance of DialogFrag is created with the default constructor, and then we use the
setArguments(Bundle b) method of Fragment to pass the arguments. Then we create the new DialogFrag using statements of the form

DialogFrag fragment = DialogFrag.newInstance(typeBar);

rather than DialogFrag fragment = new DialogFrag( ).

A fragment behaves in many respects like an Activity, but it cannot function separate from the activity in which it is embedded. One implication is that, unlike in an Activity where we can inflate views and get handles to them from the onCreate( ) method, for a subclass of Fragment we must wait for the
onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) method that FragmentDialog inherits from Fragment to be called, indicating the the fragment within the activity is ready to lay out its views. (This method will generally be called after onCreate( ).) Thus we use onCreate( ) primarily to set some styles and use onCreateView( ) to inflate the views and get handles to them. In onCreateView( )

The argument inflater is an instance of
LayoutInflater that will inflate the views for the FragmentDialog using the
inflate( ) method of LayoutInflater.

The argument container is the ViewGroup that will contain the FragmentDialog.

The argument savedInstanceState is either null or, if the fragment is being reconstructed from a previous saved state, the bundle of saved properties for the previous state.

Once the view is inflated, we use the findViewById( ) method of View to get a handle to the progress bars defined in the layout files progress_bar_only.xml and progress_bar_horiz_only.xml.

The inner class ProgressThread subclasses Thread. We use an instance of this class to run a background thread that simulates a delay when the progress dialog is displayed. This class uses a
Handler to update the views in the DialogFrag defined on the main thread, as described below. These updates supply the "progress" that is displayed by the progress bars, and finally cause the DialogFrag to be closed and the new content that we were waiting for to be displayed.

We define the
Handler
object handler using an inner class. This uses the Handler method
handleMessage(Message msg), where
Message
defines a message containing a description and arbitrary data object that can be sent to a Handler.

The chained expression msg.getData( ).getInt("total") first uses the
method
getData() that msg inherits from Message to return a
Bundle
of data associated with the message (that is sent by the class ProgressThread discussed below),
and then the Bundle method
getInt (String key)
extracts from the Bundle the integer associated with the key "total" and assigns it to the variable total.

We then use the ProgressBar method
setProgress(int value)
to set the progress bar to a value of total. Note that we don't have to do anything to cause the progress bar to reflect this update on the screen; ProgressBar is managing that.

Finally we do some logic on total to see if it has reached the maximum value for the progress bar and close the progress dialog and stop the update thread if it has.

The inner class ProgressThread implements our thread to update the progress bar by extending
Thread and
overriding its
run()
method.

After introducing some variables we define a constructor ProgressThread(Handler h) that allows
the Handler defined on the creating thread (see the invocation of the constructor in the onCreateView( ) method above) to be passed to the instance of ProgressThread as the argument h, and we set the local Handler variable mHandler in the instance of ProgressThread equal to h.

We then override the run() method of Thread, setting the
integer variable mState equal to RUNNING and implementing a while-loop that executes as long as mState = RUNNING.

In the while-loop we count the variable total up from its initial value of
zero to a maximum of maxBarValue.

We control the speed of this countdown (approximately) by calling within the loop the static method
Thread.sleep(long delay),
where delay is the requested delay in milliseconds. The actual amount of delay is under system control, so the delay each time through the loop will not be precise but is likely to be close to the requested amount. Since the sleep(long delay) method throws InterruptedException, the Thread.sleep(delay) request must be wrapped in a try-catch clause to catch the exception.

Each time through the loop, we send a message to the Handler object on the main thread. We place in the data Bundle of the message any data that we wish to communicate to the main thread. In this simple example we send only the current value of total, referenced by the key "total".

Although the Message class has a public constructor, it is more efficient to create new messages by using either the static
Message.obtain()
method or the Handler method
obtainMessage()
to get one. These methods
will allocate new messages from a pool of recycled objects, which is more
efficient than creating and allocating new instances. In this case
mHandler.obtainMessage() is used to create the new Messagemsg.

Then, we use the Handler method
sendMessage(Message msg) to push the message onto the end of the message queue that will be received by the Handler object handler on the main thread through its handleMessage(Message msg) method. This message will cause the progress bar defined on the main UI thread to update each time though the while-loop that is executing on the second thread.

Finally, we define the method setState(int state) to change the value of mState and thus to
stop execution of the while-loop in the second thread when we are finished.

In this example the updating task was a trivial one and it wasn't really essential to move the update to a separate thread. But this simple case serves as a prototype of how to update progress to the UI thread from a secondary thread for cases where the task on the second thread might not be so trivial.

The Class ProgressExample

The class ProgressExample defines an
Activity
into which we insert a
ProgressBar. Because this is an activity, we can set the progress bar views and get handles to the progress bars in the onCreate( ) method, unlike in the DialogFragment described above where we had to wait for the onCreateView( ) method to be called.

Since in these examples we wish the progress bar to be displayed inline and to be replaced by the content of a TextField, we define both the ProgressBar and Textfield in the same layout file (e.g., see progbar.xml), and use their setVisibility( ) methods (which both inherit from View) to control whether the progress bar or the textfield is visible at a given time. In these examples we also use a background thread with delay to simulate the progress bar display update as in DialogFrag, but here we do it in a more compact way using an anonymous inner class.

Themes

The looks of the progress bars and dialog windows are controlled by styles that are set in
res/values/styles.xml (see the
Using Themes project for further discussion). In particular, the progress bars used in this project are defined by the styles

where each style definition begins with an existing ProgressBar style specified by the parent attribute and adds additional properties to it using the item tags. Thus, for example, in the layout file res/layout/progbar_horiz_only.xml the style of the progress bar is specified as the ProgressBar.Horizontal.Bar custom style defined above, which gives a determinate horizontal bar with the Holo Light look.
To see various options for ProgressBar styles that can be used as a starting point for custom styles, go to
R.style and search on "ProgressBar" in the resulting webpage.

To get the Holo theme look displayed in the device shots shown above, I found it necessary to define these custom styles in res/values/styles.xml based on
this discussion. When using the default ProgressBar styles, my devices running Android 4.4.2 were defaulting to the older non-Holo styles for progress bars, even though the overall style of the project is set to Holo Light (when tested in March, 2014). The expected Holo theme was showing up for progress bars defined using the older deprecated methods to create progress bars, but not with the newer recommended methods used here, except by employing the above custom styles.

Custom Drawables

The custom progress bar displayed by pressing the fifth button on the main interface is implemented by overriding the default
Drawable objects
that Android uses to display the progress bar graphics (see the
Drawable Resources document). These custom drawables are defined in res/drawable-hdpi/custom_progress_bar.xml (which, you will note, is not the usual location for an XML file in an Android project).
There we define two new shapes filled by color gradients, one for the background and one for the bar itself. The tags used to implement these shapes are described in the
Shape and
Clip
subsections of the Drawable Resources document.
The custom drawables are then substituted for the defaults through the code