Murat Can ALPAY
Murat Can ALPAY

Reputation: 530

Changing the image of the ImageButton when the onClick is called

I have an ImageButton which performs an long running operation when clicked. So what I am unsuccessfully trying to do is change the image at the beginning of the onClick call and change it back at the end.

I have tried the below code but what I found is android redraws the button image after the onClick is finished so it's no use.

I am also open to other ways to do this if possible.

Below is the code piece I use. Thread.sleep represents the long running operation. I would like to see the image change when the execution reaches the sleep not after the onClick is finished.

btnTest = (ImageButton) findViewById(R.id.btnTest);
btnTest.setOnClickListener(new OnClickListener() {
@Override
public void onClick(View v) {
    System.out.println("start");
    btnTest.setImageResource(R.drawable.add);
    try {
        Thread.sleep(1000);
    } catch (InterruptedException e) {
    }

    System.out.println("end");
    btnTest.setImageResource(R.drawable.del);
}
});

Upvotes: 1

Views: 2471

Answers (2)

class stacker
class stacker

Reputation: 5347

Your problem is that you sleep() on (or otherwise block) the UI thread. Please understand that the main/UI thread is responsible for redraws and all event handling. If you sleep there (onClick() is executed ion the UI thread), then Android will be unable to do anything user interface related.

The background is that a multithreaded GUI would be extremely inefficient. Most UI related calls, such as setBackground, simply set some data or even schedule some more activities, have no immediate effect and will only yield results if the control returns to the main UI thread control loop, e.g. for a coordinated redraw. Just imagine what would happen if you launched a couple of layout changes one after another and each one would be processed immediately -- that would result in a huge number of computations which would become invalid right the next moment.

So the main thread is nothing but a huge loop processing all kinds of callbacks, including Activity lifecycle methods, event listener callbacks etc. So the UI is single threaded if you will.

What you need to do is perform your long-running operation on an AsyncTask or Thread. For starters, I'd try an AsyncTask. In the AsyncTask's onPostExecute() method, which is run on the UI thread again, you can manipulate your user interface as you wish.

For a quick success, you can also use a Thread:

@Override
public void onClick(View v) {
    // on UI thread
    btnTest.setImageResource(R.drawable.add);
    // launch concurrent thread
    (new Thread(new Runnable() {
            @Override
            // the code of this method will run concurrently
            public void run() {
                System.out.println("start");
                try {
                    Thread.sleep(1000);
                } catch (InterruptedException e) {}
                System.out.println("end");
                // at this point, we need to get back to the UI thread
                // for simplicity, we use the view's convenience method for that
                v.post(new Runnable() {
                    @Override
                    // the code of this method will run on the UI thread
                    public void run() {
                       btnTest.setImageResource(R.drawable.del);
                    }
                })
            }
        })
    ).start();
}

AsyncTask looks much nicer but it's not easily created and used on-the-fly.

Please note that the code above creates a Thread which will run until either it leaves its run() method, or Android kills the Linux process which hosts your app.

Also note that this Thread holds references to a lot of objects from your code, because it can access everything from its surrounding scope.

The conclusion is, that if your Thread does run for a long time, it could hold references to a View which is not even visible anymore, so zombie memory objects could worsen the resource consumption of your app.

Also, you mentioned that you're doing a long-running operation there. The question is, does that operation wait or does it compute something. If it computes something, you may find that it now takes ten times longer unless you adjust the AsyncTask/Thread priority as described here.

Upvotes: 6

SKK
SKK

Reputation: 5271

I hope you will be doing some operation in onclick instead of just Thread.Sleep().

In that case you could use the following approach using AsyncTask.

btnTest = (ImageButton) findViewById(R.id.btnTest);
btnTest.setOnClickListener(new OnClickListener() {

   @Override
   public void onClick(View v) {

       new SomeLongRunningOperationAsyncTask().execute();

   }
});

AsyncTask class for the long running process.

class SomeLongRunningOperationAsyncTask extends AsyncTask<Void, Void, Void>
{

    @Override
    protected void onPreExecute() {

        super.onPreExecute();
        System.out.println("start");
        btnTest.setImageResource(R.drawable.add);

    }

    @Override
    protected Void doInBackground(Void... params) {

        //your long running operation.
        return null;
    }

    @Override
    protected void onPostExecute(Void result) {

        super.onPostExecute(result);
        System.out.println("end");
        btnTest.setImageResource(R.drawable.del);
    }
}

Upvotes: 1

Related Questions