我的问题:程序在我的方案中延迟的有效方式。
我的情景: 我正在为一个简短的项目制作一个问答游戏,长话短说。当用户按下具有答案的四个J按钮中的一个时,回答问题。然后我的quizengine类调用这里显示的paintbutton方法。之后,它继续调用一些其他方法来负责使下一个问题出现。现在我想要的是使按钮在两种颜色之间切换,时间间隔减少。
到目前为止我尝试了什么: 首先,我在paintbutton方法和将接口更改为下一个问题的方法之间放置了一个JoptionPane,以查看该按钮是否会改变颜色。它成功了。当然,这不是我的意图,我只想要一个时间间隔。 然后我尝试使用Thread.Sleep。虽然程序会在更改为下一个问题之前等待,但颜色变化不可见。 最后我尝试了一个Timer thingy(可能不正确)的实现,虽然它改变了程序的颜色,然后继续下一个问题。
我想成为代码
/* Paints the button pressed red or green
* Green if the anwser is correct red if
* its false
*
* @param int bnr the number of the button
* @param boolean corr true if correct false if false :D
*/
public static void paintbutton(int bnr,boolean corr) {
for (int i=10;i>1;i--){
b[bnr-1].setBackground(null);
//wait for i*100 milliseconds
b[bnr-1].setBackground(corr?Color.green:Color.red);
}
}
答案 0 :(得分:3)
让我们从一些基础知识开始。 Swing是单线程的,这意味着你必须永远不要在事件调度线程的上下文中阻塞或执行长时间运行的代码,这将使UI冻结并且用户不安
Swing不是线程安全的,这意味着你永远不应该从EDT的上下文之外创建或更新UI。
这会导致您遇到问题。在稍微延迟之后,您希望更新UI。
幸运的是,至少有两种可能性,即Swing Timer
或SwingWorker
。
Swing Timer
相对简单,但实际上并没有提供在更新之间生成可变延迟的方法。 SwingWorker
更复杂,但可以让你控制基本上做你想要的。
这两个(可以)在EDT之外“等待”,但两者都提供了可以安全地将更新推送到EDT的方法。
例如......
import java.awt.Color;
import java.awt.Dimension;
import java.awt.EventQueue;
import java.awt.GridBagLayout;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.beans.PropertyChangeEvent;
import java.beans.PropertyChangeListener;
import java.util.List;
import javax.swing.JButton;
import javax.swing.JFrame;
import javax.swing.JPanel;
import javax.swing.SwingWorker;
import javax.swing.UIManager;
import javax.swing.UnsupportedLookAndFeelException;
public class BlinkyTheButton {
public static void main(String[] args) {
new BlinkyTheButton();
}
public BlinkyTheButton() {
EventQueue.invokeLater(new Runnable() {
@Override
public void run() {
try {
UIManager.setLookAndFeel(UIManager.getSystemLookAndFeelClassName());
} catch (ClassNotFoundException | InstantiationException | IllegalAccessException | UnsupportedLookAndFeelException ex) {
ex.printStackTrace();
}
JFrame frame = new JFrame("Testing");
frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
frame.add(new TestPane());
frame.pack();
frame.setLocationRelativeTo(null);
frame.setVisible(true);
}
});
}
public class TestPane extends JPanel {
private JButton blinky;
public TestPane() {
blinky = new JButton("Blinky");
blinky.setOpaque(true);
blinky.addActionListener(new ActionListener() {
private BlinkWorker worker;
@Override
public void actionPerformed(ActionEvent e) {
if (worker == null || worker.getState() == SwingWorker.StateValue.DONE) {
worker = new BlinkWorker(blinky, Color.RED);
worker.addPropertyChangeListener(new PropertyChangeListener() {
@Override
public void propertyChange(PropertyChangeEvent evt) {
SwingWorker worker = (SwingWorker) evt.getSource();
if ("state".equals(evt.getPropertyName())) {
if (worker.getState() == SwingWorker.StateValue.DONE) {
// this is where you would then call the method to
// update the state for a new question
}
}
}
});
worker.execute();
}
}
});
setLayout(new GridBagLayout());
add(blinky);
}
@Override
public Dimension getPreferredSize() {
return new Dimension(200, 200);
}
public class BlinkWorker extends SwingWorker<Void, Color> {
private JButton btn;
private Color color;
private Color normal;
public BlinkWorker(JButton btn, Color color) {
this.btn = btn;
normal = btn.getBackground();
this.color = color;
}
@Override
protected Void doInBackground() throws Exception {
for (int index = 10; index > 1; index--) {
publish(color);
Thread.sleep(index * 100);
publish(normal);
Thread.sleep(index * 100);
}
return null;
}
@Override
protected void process(List<Color> chunks) {
Color color = chunks.get(chunks.size() - 1);
btn.setBackground(color);
}
}
}
}
有关详细信息,请查看Concurrency in Swing,How to use Swing Timers和Worker Threads and SwingWorker
答案 1 :(得分:0)
找到了解决方法! 好吧我不知道我的解决方案有多少级错误,但它似乎可以解决问题。我注意到如果使用JOptionPane.showMessageDialog,按钮正在正常绘制。现在下一个问题是用户不必按对话框上的OK按钮......所以我使用了一个机器人!这是代码:
public static void paintbutton(int bnr,boolean corr) {
long time;
try {
Robot robot = new Robot();
for (int i=5;i>1;i--){
b[bnr-1].setBackground(null);
// Simulate a key press
robot.keyPress(KeyEvent.VK_SPACE);
robot.keyRelease(KeyEvent.VK_SPACE);
JOptionPane.showMessageDialog(null,"hi");
time = System.currentTimeMillis();
do {
}while(time+i*100>System.currentTimeMillis());
b[bnr-1].setBackground(i==1?(corr?Color.green:Color.red):Color.yellow);
// Simulate a key press
robot.keyPress(KeyEvent.VK_SPACE);
robot.keyRelease(KeyEvent.VK_SPACE);
JOptionPane.showMessageDialog(null,"hi");
time = System.currentTimeMillis();
do {
}while(time+i*100>System.currentTimeMillis());
}
} catch (AWTException e) {
System.err.println("error");
}
}
好吧它很乱,消息对话框短暂出现,但由于游戏框架的位置隐藏了。如果你移动主框架它会短暂出现等等。但似乎我在这里的东西。创建JOptionPane时发生的事情会使按钮重新绘制。如果有人知道如何重新创建创建JOptionPane时发生的事情而不必创建它,那就太棒了!