Flashing buttons with Thread

Asked

Viewed 560 times

4

I’m having a problem trying to make the buttons blink.

Using the Thread.sleep(), by clicking the button, Thread.sleep() ignores what comes before it, executes the sleep and performs only what comes next, in this case the setBackground(Color.GRAY). I’ve looked for solutions here, and either I didn’t understand what they meant or didn’t work. I also tried with the class Timer, but I could not.

package exstackoverflow;

import java.awt.Color;
import java.awt.Container;
import java.awt.GridLayout;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.util.ArrayList;
import java.util.Random;
import javax.swing.JButton;
import javax.swing.JFrame;

public class ExStackOverflow extends JFrame{

    JButton b = new JButton();
    JButton b2 = new JButton();
    JButton b3 = new JButton();
    JButton b4 = new JButton();
    int numBotao=0;

    public ExStackOverflow(){

         Container c = getContentPane();
        c.setLayout(new GridLayout(2,2));

        b.setBackground(Color.GRAY);
        b2.setBackground(Color.GRAY);
        b3.setBackground(Color.GRAY);
        b4.setBackground(Color.GRAY);

        b.addActionListener(new botaoListener());
        b2.addActionListener(new botaoListener());
        b3.addActionListener(new botaoListener());
        b4.addActionListener(new botaoListener());

        c.add(b);
        c.add(b2);
        c.add(b3);
        c.add(b4);
        numBotao = new Random().nextInt(4);

        piscar();

        setSize(300,300);
        setVisible(true);
        setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
    }

    class botaoListener implements ActionListener{

        @Override
        public void actionPerformed(ActionEvent ae) {
             if(ae.getSource().equals(b)){
                 numBotao=1;
                 piscar();

             }else if(ae.getSource().equals(b2)){
                 numBotao=2;
                 piscar();

             }else if(ae.getSource().equals(b3)){
                 numBotao=3;
                 piscar();

             }else if(ae.getSource().equals(b4)){
                 numBotao=4;
                 piscar();
             }
        }
    }

     public void piscar(){
        try{
            switch(numBotao){//switch baseado nos valores inseridos na lista Sequencia
                case 1:
                    b.setBackground(Color.BLUE);
                    Thread.sleep(1500);
                    b.setBackground(Color.GRAY);
                    break;

                case 2:
                    b2.setBackground(Color.RED);
                    Thread.sleep(1500);
                    b2.setBackground(Color.GRAY);
                    break;
                case 3:
                    b3.setBackground(Color.YELLOW);
                    Thread.sleep(1500);
                    b3.setBackground(Color.GRAY);
                    break;
                case 4:
                    b4.setBackground(Color.GREEN);
                    Thread.sleep(1500);
                    b4.setBackground(Color.GRAY);
                    break;
            }
        }catch(Exception e){

        }
    }

    public static void main(String[] args) {
        new ExStackOverflow().piscar();

    }

}
  • 2

    Please add a [mcve] so that it is possible to simulate the problem.

  • Another thing, you need to work with swingworker to do this, because in swing, the entire graphical interface runs on top of a thread called Event-Dispatch-thread, and the screen only updates after finishing the changes in this interface. That is, when the EDT updates its screen, the two changes will have already been made and will not flash the component.

  • the idea was to make the game Genius. There are already several questions for this but I saw a solution that worked. And also it’s the first time I see someone talk about swingworker.

  • 1

    I understand, but without one [mcve] it’s even hard to tell if swingworker is what you need.

  • diegofm, I edited and put a new example. Please see if it’s enough to help you understand the problem.

  • my answer answered you? You can accept it by clicking on , this way it will serve as reference for other users. :)

Show 1 more comment

1 answer

4


Note: The swing interface should always be executed within the Event Dispatch Thread (or EDT), for this you should always start a window in the main, in the form below:

public static void main(String[] args) {

    EventQueue.invokeLater(new Runnable() {
        @Override
        public void run() {
           new ExStackOverflow().piscar();
        }
    });
}

Or using lambda, in Java 8:

public static void main(String[] args) {

    EventQueue.invokeLater(() -> {
        new ExStackOverflow().piscar();
    });
}

When you run an interface on , it is executed in a Thread itself called Event-Dispatch-thread, and this Thread stays in loop awaiting modifications or actions in the interface. Thanks to this way of working, you can apply actions on buttons and other components. Because of this particular functioning, when you do

b.setBackground(Color.BLUE);
Thread.sleep(1500);
b.setBackground(Color.GRAY);

To EDT will not run each line and display on the interface, but apply all modifications and redesign the interface. In this case you will still cause a Freeze on the interface, because of this Thread.sleep, this will only delay the redesign of the interface by EDT.

What you need in this case is to perform the action of piscar() parallel to EDT In order to achieve this, you need a new Thread running this. Fortunately, in java itself there is already a class to work with EDT, is called SwingWorker.

You need to run the method in this class piscar() so that it works as desired:

public void piscar() {
    SwingWorker worker = new SwingWorker() {
        @Override
        protected Object doInBackground() throws Exception {

            switch (numBotao) {//switch baseado nos valores inseridos na lista Sequencia
                case 1:
                    b.setBackground(Color.BLUE);
                    Thread.sleep(300);
                    b.setBackground(Color.GRAY);
                    break;

                case 2:
                    b2.setBackground(Color.RED);
                    Thread.sleep(300);
                    b2.setBackground(Color.GRAY);
                    break;
                case 3:
                    b3.setBackground(Color.YELLOW);
                    Thread.sleep(300);
                    b3.setBackground(Color.GRAY);
                    break;
                case 4:
                    b4.setBackground(Color.GREEN);
                    Thread.sleep(300);
                    b4.setBackground(Color.GRAY);
                    break;
            }
            return null;
        }

        @Override
        protected void done() {

            super.done();
            try {
                get();
            } catch (InterruptedException | ExecutionException ex) {
                ex.printStackTrace();
            }
        }
    };
    worker.execute();
}

In this class there is an abstract method called doInBackground() (which is the method where parallel execution happens), and it is in it that you will implement the blinking action, as was done in the above code.

I implemented the method done(), because it is invoked when the execution within the doInBackground ends, and called the method get() in it so that in case of any problem that throws an exception, it is possible to capture it and detect the problem, because as the doInBackground() runs in another Thread and at the end returns to the EDT, would not be able to detect problems that may occur without calling the get().

Finally, the execute() to do the SwingWorker work.

When making the suggested changes and executing your code, this will be how:

Animação ccom o resultado

  • +1, very good answer. But I would like to eliminate this switch horrific (although this question is old, I don’t know if it would make sense to do it today).

  • @Victorstafusa good, it is worth improving anyway :) but how to remove the switch? Turning into ENUM?

  • First create a private static Color[] BUTTON_COLORS = {Color.BLUE, Color.RED, Color.YELLOW, Color.GREEN}; Then replace the method with the switch for @Override&#xA; protected Object doInBackground() throws Exception {&#xA; if (numBotao >= 1 && numBotao <= 4) {&#xA; b.setBackGround(BUTTON_COLORS[numBotao - 1]);&#xA; Thread.sleep(300);&#xA; b.setBackground(Color.GRAY);&#xA; }&#xA; return null;&#xA; }

Browser other questions tagged

You are not signed in. Login or sign up in order to post.