0

After click button I would like to change its color, then wait one second and change its color back.

This is my code:

public void click(final View view) throws InterruptedException {

    final Button btn = findViewById(view.getId());

    btn.setBackgroundColor(Color.parseColor("#0000ff"));
    btn.setClickable(false);

    Thread t = new Thread(new Runnable() {
        @Override
        public void run() {
            try {
                Thread.sleep(1000);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
        }
    });

    t.start();
    t.join();

    btn.setBackgroundColor(Color.parseColor("#e2e2e2"));
    btn.setClickable(true);
}

It doesn't work. I've checked it with more complex code and debugger and it looks like all UI changes are made collectively after finish this function.

I've found this thread: apply ui changes immediately and tried to put setBackgroundColor() and setClickable() into runOnUiThread function:

runOnUiThread(new Runnable() {
    @Override
    public void run() {
        btn.setBackgroundColor(Color.parseColor("#0000ff"));
        btn.setClickable(false);
    }
});

But it also doesn't work. What should I do?

Reaz Murshed
  • 23,691
  • 13
  • 78
  • 98
Robin71
  • 383
  • 5
  • 26

5 Answers5

2

Something like this :

private final Handler handler = new Handler();

public void click(final View view) {
      view.setBackgroundColor(Color.parseColor("#0000ff"));
      view.setClickable(false);
      handler.postDelayed(() -> {
            view.setBackgroundColor(Color.parseColor("#e2e2e2"));
            view.setClickable(true);
      }, 1000);
}

@Override protected void onPause() {
      super.onPause();

      handler.removeCallbacks(null);
}
Mark
  • 9,604
  • 5
  • 36
  • 64
1

The question is not very clear. However, I am trying to summarize the question that I have understood from your question.

You are trying to set a button's background color on clicking on it and change it back after some time. If this is the situation, then I think your idea of how threads work is wanting.

In your code, the button will change the color immediately as the sleep that you are using is running in another thread (other than UI thread). The code is executed correctly, however, you cannot see the effect of the Thread.sleep as its running in a separate thread.

So all you need to do here is to change the background color again inside the thread. Modify your code like the following.

public void click(final View view) throws InterruptedException {

    final Button btn = findViewById(view.getId());

    btn.setBackgroundColor(Color.parseColor("#0000ff"));
    btn.setClickable(false);

    Thread t = new Thread(new Runnable() {
        @Override
        public void run() {
            try {
                Thread.sleep(1000);
                btn.setBackgroundColor(Color.parseColor("#e2e2e2"));
                btn.setClickable(true);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
        }
    });

    t.start();
}

This should work.

I have created a demo trying to show what the code will do.

enter image description here

However, using Handler in case of updating UI elements in this specific case is recommended. Please see the comments below.

public void click(final View view) throws InterruptedException {

    final Button btn = findViewById(view.getId());

    btn.setBackgroundColor(Color.parseColor("#0000ff"));
    btn.setClickable(false);

    Handler handler = new Handler(); 
    handler.postDelayed(new Runnable() {
        @Override 
        public void run() { 
            btn.setBackgroundColor(Color.parseColor("#e2e2e2"));
            btn.setClickable(true);
        } 
    }, 1000); 
}
Reaz Murshed
  • 23,691
  • 13
  • 78
  • 98
  • Can you access elements of the UI in a non-handler thread? Won't this throw a CalledFromWrongThreadException? – Levi Moreira May 08 '18 at 19:35
  • Amongst other things this could easily leak the Activity or throw a null pointer inside the `Runnable` if you rotate or navigate away whilst the Thread is sleeping. – Mark May 08 '18 at 19:51
  • Yes, that might. However, I think the question is basically lacks the understanding of the thread. Thanks for keeping up the points with other details. – Reaz Murshed May 08 '18 at 19:54
  • A `Thread` is the wrong Object to use - you should be "posting" `Runnable`s on the main ui's `Looper` (message queue) - creating a `Handler` automatically associates itself with calling threads `Looper` if one exists - in this case the UI Thread – Mark May 08 '18 at 19:58
  • Yes, you are right. Using `Handler` in this specific case while updating UI elements are the primary concern is preferable. – Reaz Murshed May 08 '18 at 20:02
  • And yet your answer uses a `Thread`? Are we trying to teach bad habits? – Mark May 08 '18 at 20:05
  • @MarkKeen, I think you need to check the comment above that I have made earlier. The question is using a thread, trying to execute a task which is needed to be done after a certain time using a `Thread.sleep`. I have simply pointed out what's the basic problem here as `Thread` is a commonly used term for new android developers. Yes of course for this specific case of updating UI elements, `Handler` should have been used and I am updating my answer suggesting that. I hope I could make myself clear and of course we are **not** trying to teach bad habits. Thanks. – Reaz Murshed May 08 '18 at 20:12
  • And yet your modified code is susceptible to the same issues as using a `Thread` - You create a new local `Handler` instance that has the `Runnable` that has an implicit reference to the `Activity`, that, if you rotate the device whilst it is waiting to execute, can't get access the `Handler` to cancel the callback, which has the `Activity` reference .. – Mark May 08 '18 at 22:09
0

Not sure why that wouldn't work, but I've done something similar with

delayHandler = new Handler();
delayHandler.postDelayed(new Runnable() {
                @Override
                public void run() {
                    runOnUiThread(new Runnable() {
                        @Override
                        public void run() {
                           //change stuff on ui 
                        }
                    });
                }
            }, 1000);

if that doesn't work the only other functional difference in my code is that instead of btn being a final Button it's a private global variable in my activity.

Ryan McCaffrey
  • 201
  • 2
  • 15
  • https://developer.android.com/reference/android/os/Handler#postdelayed. It's redundant to add runOnUiThread if you created the handler in the UI thread already – Levi Moreira May 08 '18 at 19:48
0

Hope the following code will help :

btn.setBackgroundColor(Color.RED); // color you want for a second
new CountDownTimer(1000, 1000) {

    @Override
    public void onTick(long arg0) {
        // TODO Auto-generated method stub

    }

    @Override
    public void onFinish() {
                    btn.setBackgroundColor(Color.BLUE); //to change back color to prior state
                }
}.start();
Sahil
  • 952
  • 1
  • 7
  • 14
0

Try this,i think it's work for you..

final Button bPdf = findViewById(R.id.pdf);
                bPdf.setOnClickListener(new View.OnClickListener() {
                        @Override
                        public void onClick(View view) {
                                bPdf.setBackgroundColor(Color.parseColor("#0000ff"));
                                new CountDownTimer(1000, 50) {
                                        @Override
                                        public void onTick(long arg0) {
                                                // TODO Auto-generated method stub
                                        }
                                        @Override
                                        public void onFinish() {
                                                bPdf.setBackgroundColor(Color.parseColor("#e2e2e2"));
                                        }
                                }.start();
                        }
                });
Atik Faysal
  • 158
  • 1
  • 13