摆动定时器仅在最后一个事件上运行
我正在尝试构建一个西蒙说游戏,它将闪烁按钮按下。我目前正试图弄清楚如何使按钮一个接一个闪烁。遵循文档(尽我所能),我编写了下面的代码,但出于某种原因,只有绿色按钮闪烁。我进一步测试了一下,发现只有btnGo
事件中的最后一种方法才能正常工作。我认为这与计时器的运行方式有关,它在计时器结束之前将红色和蓝色按钮变回黑色,但我不确定如何或为什么?摆动定时器仅在最后一个事件上运行
public void flashRed(){
btn1.setBackground(Color.red);
btn2.setBackground(Color.black);
btn3.setBackground(Color.black);
btn4.setBackground(Color.black);
repaint();
t.start();
}
public void flashYellow(){
btn1.setBackground(Color.black);
btn2.setBackground(Color.yellow);
btn3.setBackground(Color.black);
btn4.setBackground(Color.black);
repaint();
t.start();
}
public void flashGreen(){
btn1.setBackground(Color.black);
btn2.setBackground(Color.black);
btn3.setBackground(Color.green);
btn4.setBackground(Color.black);
repaint();
t.start();
}
public void flashBlue(){
btn1.setBackground(Color.black);
btn2.setBackground(Color.black);
btn3.setBackground(Color.black);
btn4.setBackground(Color.blue);
repaint();
t.start();
}
@Override
public void actionPerformed(ActionEvent event) {
if(event.getSource() == btnGo)
{
flashRed();
flashBlue();
flashGreen();
}
if(event.getSource() ==t){
btn1.setBackground(Color.black); //resets btn1 to black
btn2.setBackground(Color.black);
btn3.setBackground(Color.black);
btn4.setBackground(Color.black);
repaint();
t.stop(); //stops the timer
}
}
有几件事情要记住:
- Swing是单线程(线程称为E(口)d(ispatch)T(hread))。当你在那个线程上做东西时,你的UI不能重画,反之亦然。
- 对
repaint()
的调用实际上并未执行重绘。它只是在美国东部时间安排重新油漆。如果安排了多个重新打印,他们只是分组,并且只有一个被执行 -
JComponents
上的大多数方法不会立即更改组件。他们只是改变组件的状态并安排重新绘制。重绘完成后,您会看到在UI中反映的更改。flashRed(); flashBlue(); flashGreen();
这会改变很多按钮的背景颜色,但根本就没有时间来进行重绘为:
所以,当你按下JButton
和你ActionListener
被触发会发生什么你的代码占据了EDT。只有当您的ActionListener
完成后,才能执行repaint
。此时,除绿色按钮外,所有按钮的背景都会变回黑色。这就是为什么你只看到绿色的按钮闪烁。
你需要做的是解决这个问题,将它们一个接一个地闪光,然后释放两者之间的EDT,给它时间来执行重绘。我会通过使用Timer
来解决这个问题。但是,为什么你只用它来将按钮的背景恢复为黑色,我也会用它来闪烁。
在伪代码中,ActionListener
会是什么样子:
switch (iteration){
case first:
flashRed();
increaseIteration();
break;
case second:
flashBlue();
increaseIteration();
break;
...
case last:
restoreAllToBlack();
timer.stop();
break;
}
你需要将所有的后台线程在等待完成,然后把以后的颜色变化命令进入EDT与调用。如果你这样做,那么你可以完全不用这个定时器构造,因为你可以睡觉()后台线程。想想这样一来,后台线程是策划的颜色变化,与EDT处理实际屏幕小部件:
import java.awt.Color;
import java.awt.Dimension;
import java.awt.GridLayout;
import java.awt.event.MouseAdapter;
import java.awt.event.MouseEvent;
import javax.swing.JButton;
import javax.swing.JFrame;
import javax.swing.SwingUtilities;
import javax.swing.SwingWorker;
public class testFrame {
static JButton btn1 = new JButton("red");
static JButton btn2 = new JButton("yellow");
static JButton btn3 = new JButton("green");
static JButton btn4 = new JButton("blue");
public static void showFrame() {
JFrame frame = new JFrame();
frame.setLocation(500, 500);
frame.setSize(100, 100);
final JButton button = new JButton("Test");
button.setMaximumSize(new Dimension(80, 30));
button.setSize(80, 20);
frame.setLayout(new GridLayout(5, 1));
frame.add(button);
frame.add(btn1);
frame.add(btn2);
frame.add(btn3);
frame.add(btn4);
button.addMouseListener(new MouseAdapter() {
@Override
public void mouseClicked(MouseEvent e) {
SwingWorker<Void, Void> worker = new SwingWorker<Void, Void>() {
@Override
public Void doInBackground() {
try {
flashRed();
Thread.sleep(500);
flashGreen();
Thread.sleep(500);
flashBlue();
Thread.sleep(500);
flashYellow();
} catch (InterruptedException e) {
}
return null;
}
@Override
public void done() {
}
};
worker.execute();
}
});
frame.pack();
frame.setVisible(true);
}
public static void flashRed() {
SwingUtilities.invokeLater(new Runnable() {
@Override
public void run() {
btn1.setBackground(Color.red);
btn2.setBackground(Color.black);
btn4.setBackground(Color.black);
btn3.setBackground(Color.black);
}
});
}
public static void flashYellow() {
SwingUtilities.invokeLater(new Runnable() {
@Override
public void run() {
btn1.setBackground(Color.black);
btn2.setBackground(Color.yellow);
btn3.setBackground(Color.black);
btn4.setBackground(Color.black);
}
});
}
public static void flashGreen() {
SwingUtilities.invokeLater(new Runnable() {
@Override
public void run() {
btn1.setBackground(Color.black);
btn2.setBackground(Color.black);
btn3.setBackground(Color.green);
btn4.setBackground(Color.black);
}
});
}
public static void flashBlue() {
SwingUtilities.invokeLater(new Runnable() {
@Override
public void run() {
btn1.setBackground(Color.black);
btn2.setBackground(Color.black);
btn3.setBackground(Color.black);
btn4.setBackground(Color.blue);
}
});
}
public static void main(String[] args) {
SwingUtilities.invokeLater(new Runnable() {
@Override
public void run() {
showFrame();
}
});
}
}
'button.addMouseListener'真的吗? “ActionListener”有什么问题。如果你在'done'方法中没有做任何事情,那么使用'SwingWorker'有什么好处呢?如果你打算用invokeLater来混淆你的代码,那么使用'SwingWorker'与Swing'Timer'相比有什么好处呢?你必须在你的代码示例中创建所有按钮静态实例 – Robin 2014-10-20 14:23:48
由于我想创建一个类应用程序,并且main()需要根据定义静态化,所以我将所有内容都设置为静态。这样可以让代码变得简单。 – DaveB 2014-10-20 20:04:14
(event.getSource()== t)是不正确的使用.equals代替 – 2014-10-18 14:07:53
你需要在每次调用之间使用Thread.sleep – subash 2014-10-18 14:32:50
@getlost如果将'ActionListener'附加到'Timer',则源代码将与您连接侦听器的实例完全相同,因此使用'=='是完全有效的代码。不需要使用“equals”。 @subash在他的代码中,我只检测EDT的用法。除非您正在设计无响应的用户界面,否则永远不要在EDT上调用Thread.sleep。 – Robin 2014-10-18 15:18:14