1

I'm having trouble understanding how the visibility of a ProgressBar works. Basically my ProgressBar won't appear or disappear when I need it to. My code is too big to show all here so I have created a new simple demo project with a button to click:

    fab.setOnClickListener(new View.OnClickListener()
    {
        @Override
        public void onClick(View view)
        {
            Snackbar.make(view, "Replace with your own action", Snackbar.LENGTH_LONG)
                    .setAction("Action", null).show();
            mProgressBar.setVisibility(View.VISIBLE);
            SystemClock.sleep(1000);
            mProgressBar.setVisibility(View.GONE);
            SystemClock.sleep(1000);
            mProgressBar.setVisibility(View.VISIBLE);
            SystemClock.sleep(1000);
            mProgressBar.setVisibility(View.GONE);
            SystemClock.sleep(1000);
            mProgressBar.setVisibility(View.VISIBLE);
         }
     }
 mProgressBar = findViewById(R.id.progressbar);
 mProgressBar.setVisibility(View.INVISIBLE);

I have also tried several versions of the Progressbar; currently it's this:

<ProgressBar
    android:id="@+id/progressbar"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    android:layout_centerVertical="true"
    android:backgroundTint="@android:color/white"
    android:gravity="center"
    android:indeterminate="true"
    android:indeterminateTint="#1a09d6"/>

So, what happens is that the ProgressBar doesn't turn on and off; it just ends up with whatever is the last state. And the SnackBar doesn't appear either, until after all of the sleeps. If I could understand how to make this snippet work as I want, then I think would have a solution to the (slightly more complex) situation in my actual code. I have read other threads and understand that a ProgressBar can't be shown or updated while the main thread is too busy. I have tried, as others have suggested, using

runOnUiThread(new Runnable() {
                @Override
                public void run() {
                    mProgressBar.setVisibility(View.GONE);
                }
            })

but this makes no difference. I have also tried View.Invalidate() between the visibility changes, but to no effect. And I've tried 'INVISIBLE' instead of 'GONE'. Clearly I'm missing something fundamental about tasks and threading here! Help please.

quilkin
  • 874
  • 11
  • 31
  • You use runOnUiThread wrong... It should be used from other thread.. – Selvin Dec 29 '20 at 21:01
  • Also it obvious why it's not working... You blocked main thread in onClick so no other main thread work is done(fx redrawing - no redraw - no visual changes) – Selvin Dec 29 '20 at 21:03
  • Ok, you say that I 'blocked the main thread' but this is an out-of-the-box new project created by Android Studio. So your comment is not a lot of help for a newbie! I have now moved the timer code into an async task and the ProgressBar shows up, thanks.But this creates a new problem ('async task should be static') which others seem to disagree about how to deal with: [link](https://stackoverflow.com/questions/44309241). What a lot of work just to show a simple progressBar! – quilkin Dec 30 '20 at 10:18
  • In my 'real' code the intensive work *was* done in async tasks, but I still couldn't get the ProgresBar to show correctly. Now realised that one task was started from 'onActivityResult()' after a file-find intent had been completed. In this case, using the 'runOnUiThread() method has solved it. – quilkin Dec 30 '20 at 10:59

1 Answers1

0

After some experimentation I have found what I was doing incorrectly. Although my code is more complex than this, in effect what I was doing was (in pseudo-code):

show progress bar
start task
hide progress bar

so it's unsurpising what results, as the bar is hidden as soon as the task starts. So, the progressBar hiding must happen in the task class itself. But then to avoid a problem with potential leaks with a non-static class, an activity 'weak reference' is required:

public static class TaskTest extends AsyncTask<Void, Void, Void> {

    private WeakReference<MainActivity> activityReference;

    TaskTest(MainActivity context) {
        activityReference = new WeakReference<>(context);
    }
    @Override
    protected void onPreExecute()
    {
        activityReference.get().mProgressBar.setVisibility(View.VISIBLE);
        super.onPreExecute();
    }
    @Override
    protected Void doInBackground(Void... voids)
    {
        // do your stuff......shouldn't be more than a few seconds.
        for (int i=0; i<4; i++)
        {
            SystemClock.sleep(1000);
         }
        return null;
    }
    protected void onPostExecute(Void result) {
        activityReference.get().mProgressBar.setVisibility(View.GONE);
        super.onPostExecute(result);
    }
}

and in the main activity:

new TaskTest(MainActivity.this).execute();

I'd be glad if someone could confirm that this is the correct approach, or if there is a better way.

quilkin
  • 874
  • 11
  • 31