2015-02-08 44 views
2

是否有任何方法將Event Dispatch Thread與由swing.TimerSwingWorkernew Thread()創建的另一個線程同步?我希望EDT等待另一個線程工作(例如,在屏幕上移動對象),並在完成此工作後立即完成一些任務。可能嗎?我必須使用另一個線程,因爲如果我嘗試在EDT中更改對象的座標,我不會在屏幕上看到移動。Swing中的同步

回答

5

你不想在EDT上同步或讓它加入其他線程,因爲這可能會凍結你的程序。可能最好的辦法是使用監聽器類型的結構來通知Swing代碼後臺線程已完成,例如可以通過將PropertyChangeListener添加到SwingWorker並監聽「狀態」屬性更改爲SwingWorker.StateValue.DONE 。如果您確實使用了這種方法,請務必在PropertyChangeListenerSwingWorker之內撥打get(),以便捕獲可能從後臺線程中拋出的所有異常。

For example


編輯
幽州的評論:

但我會盡量解釋。用戶按下按鈕。作爲按鈕按下的結果,我想讓一些JPanel對象不斷移動到屏幕上的另一個地方。這個動作大約需要0.4秒,而我真的不希望任何按鈕在這段時間內響應。運動由座標變化+ Thread.sleep(5)提供。似乎不可能在事件調度線程中實現這種動作。我對嗎?

  1. 是的,它是完全可能的,如果你使用一個Swing定時器做就EDT運動。擺動計時器使用背景線程在場景後面延遲地重複調用其ActionListener的actionPerformed方法。其使用的關鍵是其actionPerformed方法中的所有代碼在EDT上被稱爲
  2. 如果您想取消激活按鈕,請通過調用setEnabled(false)將按鈕設置爲禁用1(或更好,它是動作)。動畫完成後撤消此操作。

例如:

import java.awt.Color; 
import java.awt.Dimension; 
import java.awt.Graphics; 
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.*; 

public class MoveBall extends JPanel { 
    private static final int PREF_W = 800; 
    private static final int PREF_H = PREF_W; 
    private static final int BALL_W = 40; 
    private static final int BALL_H = BALL_W; 
    private static final Color BALL_COLOR = Color.red; 
    public static final int TIMER_DELAY = 20; 
    private int ballX = BALL_W/2; 
    private int ballY = BALL_H/2; 
    private BufferedImage ballImg; 
    private ButtonAction buttonAction = new ButtonAction("Start Animation"); 
    private Timer timer; 

    public MoveBall() { 
     ballImg = new BufferedImage(BALL_W, BALL_H, BufferedImage.TYPE_INT_ARGB); 
     Graphics2D g2 = ballImg.createGraphics(); 
     g2.setRenderingHint(RenderingHints.KEY_ANTIALIASING, 
      RenderingHints.VALUE_ANTIALIAS_ON); 
     g2.setColor(BALL_COLOR); 
     g2.fillOval(1, 1, BALL_W - 2, BALL_H - 2); 
     g2.dispose(); 

     add(new JButton(buttonAction)); 
    } 

    @Override 
    protected void paintComponent(Graphics g) { 
     super.paintComponent(g); 
     if (ballImg != null) { 
     int x = ballX - BALL_W/2; 
     int y = ballY - BALL_H/2; 
     g.drawImage(ballImg, x, y, null); 
     } 
    } 

    @Override 
    public Dimension getPreferredSize() { 
     if (isPreferredSizeSet()) { 
     return super.getPreferredSize(); 
     } 
     return new Dimension(PREF_W, PREF_H); 
    } 

    private class ButtonAction extends AbstractAction { 
     public ButtonAction(String name) { 
     super(name); 
     int mnemonic = (int)name.charAt(0); 
     putValue(MNEMONIC_KEY, mnemonic); 
     } 

     @Override 
     public void actionPerformed(ActionEvent e) { 
     ballX = BALL_W/2; 
     ballY = BALL_H/2; 
     repaint(); 
     setEnabled(false); 
     new Timer(TIMER_DELAY, new TimerListener()).start(); 
     } 
    } 

    private class TimerListener implements ActionListener { 
     @Override 
     public void actionPerformed(ActionEvent e) { 
     if (ballX + BALL_W/2 >= getWidth()) { 
      stopBall(e); 
     } else if (ballY + BALL_H/2 >= getHeight()) { 
      stopBall(e); 
     } else { 
      ballX++; 
      ballY++; 
     } 
     repaint(); 
     } 

     private void stopBall(ActionEvent e) { 
     buttonAction.setEnabled(true); 
     ((Timer) e.getSource()).stop(); 
     } 
    } 

    private static void createAndShowGui() { 
     MoveBall mainPanel = new MoveBall(); 

     JFrame frame = new JFrame("MoveBall"); 
     frame.setDefaultCloseOperation(JFrame.DISPOSE_ON_CLOSE); 
     frame.getContentPane().add(mainPanel); 
     frame.pack(); 
     frame.setLocationByPlatform(true); 
     frame.setVisible(true); 
    } 

    public static void main(String[] args) { 
     SwingUtilities.invokeLater(new Runnable() { 
     public void run() { 
      createAndShowGui(); 
     } 
     }); 
    } 
} 
+3

肯定要走的路。使用'SwingWorker'時,在處理結束時會調用'done()'方法 - 這使得在那裏調用回調變得非常容易。 – 2015-02-08 22:26:29

+0

@BoristheSpider:true,但是如果你這樣做,SwingWorker必須對調用代碼有一定的瞭解,因爲它必須調用調用代碼的方法來通知它完成,從而增加耦合。出於這個原因,我通常更喜歡使用PropertyChangeListener。 – 2015-02-08 22:27:22

+0

公平點。使用監聽器確實意味着耦合是單向的。無論哪種方式打敗同步在EDT雖然... – 2015-02-08 22:28:38