How to show and hide one view from diffrent threads in java?

925 Views Asked by At

I write my first java program for Android which draws some 3D functions (f(x,y)) using OpenGL, and the program is almost done but I experience problems with showing and hiding custom progress bar, developed as LinearLayout (which I created) with some text and progressBar. I need do make progressBarLL visible before and during calculating 3dfunction values. To do this I implemented a handler in Graph3dActivity:

public class Graph3dAct extends Activity {
    (...)
    static ProgressBar progressBarPB;
    static LinearLayout progressBarLL;
    public static Handler progressHandler = new Handler() {
        @Override
        public void handleMessage(Message msg) {
            Log.e("HANDLER:",Integer.toString(msg.arg1));
            //toneG.startTone(ToneGenerator.TONE_CDMA_ALERT_CALL_GUARD, 500);             
            switch (msg.arg1)
            {
            case 1: progressBarLL.setVisibility(LinearLayout.VISIBLE);
                    break;
            case 2: progressBarLL.setVisibility(LinearLayout.INVISIBLE);
                    break;
            case 3: progressBarPB.setProgress(msg.arg2);
                    //to be implemented
                    break;
            default: Log.e("Graph3dAct","Handler misused;");
            }
            progressBarLL.invalidate();
            progressBarPB.invalidate();           
      }
    };
    @Override
    public void onCreate(Bundle savedInstanceState) {        
        super.onCreate(savedInstanceState);
        (...)
        progressBarPB = (ProgressBar) findViewById(R.id.surface3dPB);
        progressBarLL = (LinearLayout) findViewById(R.id.progressLL);
        (...)
        final Button moreStepsB = (Button) findViewById(R.id.buttonMoreS);
        moreStepsB.setOnClickListener(new View.OnClickListener() {          
            @Override
            public void onClick(View v) {  
                int max=stepsVals.length-1;
                if (stepsIndex<max) 
                {
                    stepsIndex++;
                    steps=stepsVals[stepsIndex];
                    FuncSurf fs = new FuncSurf(steps);
                    renderer.funcSurf =  fs;

Then I have FuncSurf with its constructor doing long-lasting calculation of surface. I need to show my progressBarLL before calculations:

public class FuncSurf {
    public FuncSurf(int steps_) {        
            Log.e("FuncSurf","CONSTRUCTOR");
            Message msg = new Message();
            msg.arg1=1; //instruct handler to show progress layout: progressBarLL
            Graph3dAct.progressHandler.sendMessage(msg);

            (...) HERE GOES LONG-LASTING CALCULATION        

            msg = new Message();
            msg.arg1=2; //instruct handler to hide progress layout: progressBarLL
            Graph3dAct.progressHandler.sendMessage(msg);    
    }

The problem is that when I call function FuncSurf constructor from onClick event implemented in above Activity, then it results in showing progressBarLL for very short time and unfortunatelly after calculation is done, not before. In other words handler handles messages after FuncSurf contructor is finished not simultaneously. This is probably because it is the same thread? But when the FuncSurf() constructor is called from Renderer everything is OK:

class MyOpenGLRenderer implements Renderer {
        @Override
        public void onSurfaceChanged(GL10 gl, int width, int height) {
            funcSurf = new FuncSurf(steps); //progressBarLL shown OK

My question is how to possibly neatly correct the code so that progressBarLL is shown before calculations in FuncSurf() constructor regardless from where I call this constructor? I cannot directly use progressBarLL.setVisibility in funcSurf because this is not GUI thread when called by onSurfaceChanged(...) above. I'm sorry I couldn't explain the problem clearer. I look forward to your help.

2

There are 2 best solutions below

2
On

Use AsyncTask, it's designed for exactly this case (and takes case of thread interaction without needing messages or anything like that).

  • onPreExecute() runs in the UI thread, makes the progress bar visible.
  • doInBackground() runs the long calculation in a background thread. Regularly calls publishProgress() to notify progress.
  • onProgressUpdate() runs in the UI thread, and advances the progress bar.
  • onPostExecute() runs in the UI thread, and makes the progress bar invisible.

See the documentation for Keeping your App Responsive, it includes a similar example (in which the long-running task is a network operation).

0
On

Meanwhile I found "Thread construction" which solves the problem for now. It forces execution of "long-time task" in separate Thread, allowing GUI thread to continue and refresh the GUI by Handler. Small modification of the first fragment of code and the program seems to work OK now:

Runnable run = new Runnable() 
{
    @Override
    public void run() {
        FuncSurf fs = new FuncSurf(steps);
        renderer.funcSurf =  fs;
    }
};       
Thread thr = new Thread(run);
thr.start();