Blinking Button for Simon Says

2020-08-05 11:30发布

问题:

I'm making a Simon says program and I need the buttons to flash when the random pattern is generated so the user knows what to enter. My problem is that I cannot get my buttons(JButtons that are images) to blink, my logic is having two buttons on top of each other, one visible and on not and then switching the buttons visibility, waiting a second and then changing it back. I've tried to use Thread.sleep(), wait(), and even busy loops to wait but none have worked. I've been told a swing timer is my best bet and that's what I would like to use. Also, I want the buttons to start blinking after a start button is clicked.

public class GamePanel extends JPanel implements ActionListener
{
  private JButton greenButton;

  private JButton startButton;
  private JButton greenBlinkButton;
  private GridBagConstraints gbc;
  private Timer buttonTimer;

GamePanel()
{
    gbc = new GridBagConstraints();
    GridBagLayout grid = new GridBagLayout();
    setLayout(grid);

    //GridBag location lets
    startButton = new JButton("Start");
    gbc.gridx = 0;
    gbc.gridy = 0;
    add(startButton, gbc);
    startButton.setPreferredSize(new Dimension(200,30));

    greenReg = new ImageIcon("src/Images/Green Button.png");
    greenBlink = new ImageIcon("src/Images/Blink Green Button.png");

    greenButton = new JButton(new ImageIcon(greenReg));

    gbc.gridx = 0;
    gbc.gridy = 1;
    add(greenButton, gbc);
    add(greenBlinkButton, gbc);


    startButton.addActionListener(this);


   //Timer
  buttonTimer = new Timer(500, this);

}

@Override
    public void actionPerformed(ActionEvent e) 
    {    

     if(e.getSource() == startButton )
     {

         greenButton.setIcon(greenBlink); 
         buttonTimer.start();
         greenButton.setIcon(greenReg); 
     }
    }

This is the code for just the first button to keep it simple.

回答1:

You appear to be trying to re-start the Timer within its own listener, something that you shouldn't be doing. Understand that the actionPerformed method within that listener will be called repeatedly, in your code every 500 milliseconds, and there is no need to try to "re-start" the timer since it's already running. Instead inside of the actionPerformed method you should have code that decides which button should change icon / state, and what icon to place into it.

For instance, consider the code below. It assumes that there are two Icons, one called greenIcon, one called darkGreenIcon, and that I want to swap these icons every 200 milliseconds. Within my Timer's actionPerformed, I'll check to see which Icon is currently showing in the button by calling getIcon() on the button. If it's a greenIcon, I'll put a darkGreenIcon in the button via setIcon(...), and visa versa, if it's a darkGreenIcon, I'll put in a greenIcon. The code could look something like:

    @Override
    public void actionPerformed(ActionEvent e) {
        // get icon from button
        Icon icon = greenButton.getIcon();

        // check if it's the green icon
        if (icon == greenIcon) {
            icon = darkGreenIcon;  // if so, make it the dark green icon
        } else {
            icon = greenIcon;  // if not, make it the green icon
        }
        greenButton.setIcon(icon);  // stuff it back into the button
    }

For example, a runnable program:

import java.awt.BasicStroke;
import java.awt.BorderLayout;
import java.awt.Color;
import java.awt.Graphics2D;
import java.awt.RenderingHints;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.awt.image.BufferedImage;
import javax.swing.*;

@SuppressWarnings("serial")
public class FlashingButton extends JPanel {
    private static final String START = "Start";
    private static final String STOP = "Stop";
    private static final int TIMER_DELAY = 200; // millisecond delay
    private static final int BI_WIDTH = 400;
    private Icon greenIcon;
    private Icon darkGreenIcon;
    private JButton greenButton = new JButton();
    private JButton startButton = new JButton(new StartAction(START));
    private Timer timer = new Timer(TIMER_DELAY, new TimerListener());

    public FlashingButton() {
        greenIcon = createMyIcon(Color.GREEN);
        darkGreenIcon = createMyIcon(Color.GREEN.darker());
        greenButton.setIcon(greenIcon);
        setBorder(BorderFactory.createEmptyBorder(40, 40, 40, 40));
        setLayout(new BorderLayout(20, 20));

        add(greenButton, BorderLayout.CENTER);
        add(startButton, BorderLayout.PAGE_END);
    }

    // Ignore this code. It simply is present to create image icons 
    // without having to use an actual image. This way you can run this code without an image
    private Icon createMyIcon(Color color) {
        BufferedImage img = new BufferedImage(BI_WIDTH, BI_WIDTH, BufferedImage.TYPE_INT_ARGB);
        Graphics2D g2 = img.createGraphics();
        g2.setRenderingHint(RenderingHints.KEY_ANTIALIASING, RenderingHints.VALUE_ANTIALIAS_ON);
        g2.setColor(color);
        g2.fillOval(5, 5, BI_WIDTH - 10, BI_WIDTH - 10);
        g2.setStroke(new BasicStroke(10f));
        g2.setColor(Color.LIGHT_GRAY);
        g2.drawOval(5, 5, BI_WIDTH - 10, BI_WIDTH - 10);
        g2.dispose();
        return new ImageIcon(img);
    }

    private class TimerListener implements ActionListener {

        @Override
        public void actionPerformed(ActionEvent e) {
            // get icon from button
            Icon icon = greenButton.getIcon();

            // check if it's the green icon
            if (icon == greenIcon) {
                icon = darkGreenIcon;  // if so, make it the dark green icon
            } else {
                icon = greenIcon;  // if not, make it the green icon
            }
            greenButton.setIcon(icon);  // stuff it back into the button
        }
    }

    // this is my startButton's Action.
    // an Action is like an "ActionListener on steroids"
    private class StartAction extends AbstractAction {
        public StartAction(String name) {
            super(name);  // the text that appears in the button
            putValue(MNEMONIC_KEY, (int) name.charAt(0));  // the alt-key mnemonic
        }

        @Override
        public void actionPerformed(ActionEvent e) {
            if (timer.isRunning()) {  // if the timer is currently running
                timer.stop();    // stop the Timer
                greenButton.setIcon(greenIcon);  // set the icon back to the defaut green icon
                putValue(NAME, START);  // change the button's text to "Start"
            } else {  // otherwise the Timer's not running
                timer.start();  // Start it
                putValue(NAME, STOP);  // change this button's text to "Stop"
            }
        }
    }

    private static void createAndShowGui() {
        JFrame frame = new JFrame("Flashing Button");
        frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
        frame.getContentPane().add(new FlashingButton());
        frame.pack();
        frame.setLocationRelativeTo(null);
        frame.setVisible(true);
    }

    public static void main(String[] args) {
        SwingUtilities.invokeLater(() -> createAndShowGui());
    }
}