【问题标题】:Jbutton flash between two background colorsJbutton 在两种背景颜色之间闪烁
【发布时间】:2016-04-09 23:52:06
【问题描述】:

我的问题:程序在我的场景中延迟的有效方法。

我的场景: 我正在为一个简短的项目制作一个问答游戏,长话短说。当用户按下四个有答案的 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);          
       }
   }

【问题讨论】:

  • 使用 Swing Timer,有关详细信息,请参阅 How to use Swing Timers
  • 我的赌注也在那里。正如我所说,它对我来说失败了,可能是因为我没有正确使用它。我现在正试图弄清楚。关于如何使用它的示例我的场景将帮助我理解。
  • 啊实际上它可能会给我带来问题,因为其余代码将继续,这将导致更改问题文本。至少这似乎是问题=/
  • 好吧,你要么使用 Swing TimerSwingWorker 来完成它,要么什么都不会重绘,你不能阻止 EDT
  • 请保持家庭友好。无需在这里发誓就可以表达您的观点。此外,您应该将您的解决方案添加为答案,而不是将其添加到您的问题中。如果你把它留在里面,它很可能会被编辑为噪音。

标签: java swing animation time intervals


【解决方案1】:

让我们从一些基础知识开始。 Swing 是单线程的,这意味着您绝不能在 Event Dispatching Thread 的上下文中阻塞或执行长时间运行的代码,这会使 UI 冻结并让用户感到不安

Swing 不是线程安全的,这意味着您永远不应从 EDT 上下文之外创建或更新 UI。

这会导致您遇到问题。您希望在稍作延迟后更新 UI。

幸运的是,至少有两种可能性,Swing TimerSwingWorker

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 SwingHow to use Swing TimersWorker Threads and SwingWorker 了解更多详情

【讨论】:

  • 哇!从我的第一次尝试开始,我觉得这样做会很棘手。我真的很感谢你花时间在这件事上。跳入实现 UI 是一个全新的棘手水平。感谢您打开我的眼睛,我会仔细研究您的答案。
【解决方案2】:

找到了解决方法! 好的,我不知道我的解决方案在多少级别上是错误的,但它似乎可以解决问题。我注意到如果使用 JOptionPane.showMessageDialog 按钮正在正常绘制。现在下一个问题是用户不必按下对话框上的确定按钮......所以我使用了机器人!代码如下:

 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 时发生的事情而不必创建它,那就太好了!

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2015-07-05
    • 2016-03-27
    • 1970-01-01
    相关资源
    最近更新 更多