Swing中的同步

Ale*_*der 2 java swing

有没有办法将Event Dispatch Thread与另一个创建的线程同步swing.Timer,SwingWorker或者new Thread()?我希望EDT等待另一个线程工作(例如在屏幕上移动对象)并在完成此工作后立即执行某项任务.可能吗?我必须使用另一个线程,因为如果我尝试在EDT中更改对象的坐标,我看不到屏幕上的移动.

Hov*_*els 5

你不想在EDT上同步或让它加入其他线程,因为这可能会冻结你的程序.可能你最好的选择是使用一个监听器类型的结构来通知Swing代码后台线程已经完成,例如可以通过添加PropertyChangeListenera SwingWorker并监听要更改的"state"属性来完成SwingWorker.StateValue.DONE.如果你确实使用它,请务必get()在你的SwingWorker内部调用,PropertyChangeListener以便捕获可能在后台线程中抛出的所有异常.

例如


编辑
你在评论中说:

但我会试着解释一下.用户按下按钮.我希望一些JPanel对象在按下按钮的同时连续移动到屏幕上的另一个位置.这个动作大约需要0.4秒,我真的不希望任何按钮在此期间响应.移动由坐标变化+ Thread.sleep(5)提供.似乎在Event Dispatch Thread中实现这一动作是不可能的.我对吗?

  1. 是的,如果使用Swing Timer,很有可能在EDT上进行移动.摆动定时器使用一个后台线程在后台反复调用它的ActionListener的actionPerformed方法有延迟.使用它的关键是它的actionPerformed方法中的所有代码都在EDT上调用.
  2. 如果要取消激活按钮,请通过调用将按钮设置为禁用1(或更好,它是动作)setEnabled(false).动画完成后撤消此操作.

例如:

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();
         }
      });
   }
}
Run Code Online (Sandbox Code Playgroud)

  • 绝对是要走的路.使用`SwingWorker`,在处理结束时,无论如何都会调用`done()`方法 - 这使得在那里调用回调非常容易. (3认同)