2014-10-18 93 views
1

我正在嘗試構建一個西蒙說遊戲,它將閃爍按鈕按下。我目前正試圖弄清楚如何使按鈕一個接一個閃爍。遵循文檔(盡我所能),我編寫了下面的代碼,但出於某種原因,只有綠色按鈕閃爍。我進一步測試了一下,發現只有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 
     } 
    } 
+0

(event.getSource()== t)是不正確的使用.equals代替 – 2014-10-18 14:07:53

+0

你需要在每次調用之間使用Thread.sleep – subash 2014-10-18 14:32:50

+0

@getlost如果將'ActionListener'附加到'Timer',則源代碼將與您連接偵聽器的實例完全相同,因此使用'=='是完全有效的代碼。不需要使用「equals」。 @subash在他的代碼中,我只檢測EDT的用法。除非您正在設計無響應的用戶界面,否則永遠不要在EDT上調用Thread.sleep。 – Robin 2014-10-18 15:18:14

回答

3

有幾件事情要記住:

  • 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; 
} 
-1

你需要將所有的後臺線程在等待完成,然後把以後的顏色變化命令進入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(); 
      } 
     }); 
    } 
} 
+0

'button.addMouseListener'真的嗎? 「ActionListener」有什麼問題。如果你在'done'方法中沒有做任何事情,那麼使用'SwingWorker'有什麼好處呢?如果你打算用invokeLater來混淆你的代碼,那麼使用'SwingWorker'與Swing'Timer'相比有什麼好處呢?你必須在你的代碼示例中創建所有按鈕靜態實例 – Robin 2014-10-20 14:23:48

+0

由於我想創建一個類應用程序,並且main()需要根據定義靜態化,所以我將所有內容都設置爲靜態。這樣可以讓代碼變得簡單。 – DaveB 2014-10-20 20:04:14