Saturday, October 16, 2010

Android and the title bar progress indicator

 

I just spent quite some time trying to figure out how to get a tiny little spinning progress thingy in the title bar of my application. And to be honest, while there is a lot of documentation out there, it is far from trivial to finally implement it.

The best "how to" is just  few weeks old and the author also wanted to have such a thing.

Basically steps are: define a custom layout for the title bar, ask the system to allow replacing the title bar, set the content layout, only then set the custom title bar layout and then obtain a reference to the ProgressBar object.

As the post above is quite extensive, I just want to emphasis on the latter points:

 

protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
   requestWindowFeature(Window.FEATURE_CUSTOM_TITLE); // 1
   setContentView(R.layout.single_tweet);        // 2
   getWindow().setFeatureInt(Window.FEATURE_CUSTOM_TITLE,
                              R.layout.window_title); // 3
   pg = (ProgressBar) findViewById(R.id.title_progress_bar); // 4

 

Comments for the lines above:

  1. Ask the system to allow for a custom window title
  2. Set the content view of the whole window
  3. set the layout for the custom title
  4. Obtain the reference to the progress bar

If this order is not honored, pg, the reference to the ProgressBar will be null.

The layout for the title is shown in above post, so I'll not repeat it here.

The second important aspect is the usage of the ProgressBar. The main thread of execution in Android is the UI thread. And when a callback is running, the UI is not updating while such a callback is running. So, the following is not starting the progress indicator:

   public void myButtonPressedCallback(View v) {
      pg.setVisibility(ProgressBar.VISIBLE);
      doSomeThingLongRunning();
      pg.setVisibility(ProgressBar.INVISIBLE);
}

The state of the progress indicator will only be set after myButton..() has returned. In oder to correclty handle this, you need to e.g. use an AsyncTask:

 

    private class DownloadImageTask extends AsyncTask<User, Void,Bitmap> {
        protected void onPreExecute() {
            super.onPreExecute();
            pg.setVisibility(ProgressBar.VISIBLE);
        }
        protected Bitmap doInBackground(User... users) {
          doSomeThingLongRunning();
        }
        protected void onPostExecute(Bitmap result) {
           pg.setVisibility(ProgressBar.INVISIBLE);
        }

 

   }

In this case, the ProgressBar is made visible in onPreExecute() (which runs in the UI thread. Then the background thread is started to do the long running computation and after this has finished, onPostExecute() runs again in the UI thread, where you can 'switch off' the ProgressBar again.

 

No comments: