为什么线程在我的actionListener实现中冻结了我的代码?

我试图通过按下按钮(向上,向下,……)顺利地制作一个椭圆形移动,我尝试使用线程与try并在课堂内捕捉它的工作但是当我在这里使用它时会冻结整个应用程序。

class movingdown implements ActionListener{ @Override public void actionPerformed(ActionEvent e){ for (int i = 0; i < frame.getWidth(); i++) { x++; frame.repaint(); try{Thread.sleep(50);}catch(Exception ex){} } } }//movingdown end 

这是我的整个代码:

 package swingWork; import java.awt.BorderLayout; import java.awt.Button; import java.awt.Color; import java.awt.Graphics; import java.awt.event.ActionEvent; import java.awt.event.ActionListener; import javax.swing.*; public class movingball { JFrame frame; int x=0; int y =0; public static void main(String []args){ movingball ball = new movingball(); ball.start(); } public void start(){ ballDrawer drawer = new ballDrawer(); frame = new JFrame(); frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE); JButton up =new JButton("UP"); JButton down =new JButton("DOWN"); JButton left =new JButton("LEFT"); JButton right =new JButton("RIGHT"); frame.getContentPane().add(BorderLayout.NORTH, up); frame.getContentPane().add(BorderLayout.SOUTH, down); frame.getContentPane().add(BorderLayout.WEST, left); frame.getContentPane().add(BorderLayout.EAST, right); frame.getContentPane().add(BorderLayout.CENTER,drawer); //setting up the action listener: up.addActionListener(new movingup()); down.addActionListener(new movingdown()); left.addActionListener(new movingleft()); right.addActionListener(new movingright()); frame.setSize(300, 300); frame.setVisible(true); int rightsize =right.getWidth(); }//start method end //here we have the listeners class movingup implements ActionListener{ @Override public void actionPerformed(ActionEvent e) { for (int i = x; i > frame.getHeight(); i--) { x=i; try{Thread.sleep(50);}catch(Exception ex){} } } }//moving up end class movingdown implements ActionListener{ @Override public void actionPerformed(ActionEvent e){ for (int i = 0; i < frame.getWidth(); i++) { x++; frame.repaint(); try{Thread.sleep(50);}catch(Exception ex){} } } }//movingdown end class movingleft implements ActionListener{ public void actionPerformed(ActionEvent e){ x=x+10; frame.repaint(); System.out.println(x); } }//moving left end class movingright implements ActionListener{ public void actionPerformed(ActionEvent e){ } }//moving right end class ballDrawer extends JPanel{ @Override public void paintComponent(Graphics g) { g.setColor(Color.white); g.fillRect(0, 0, this.getWidth(), this.getHeight()); g.setColor(Color.black); g.fillOval(x, y, 100, 100); }//PaintComponents End }//ball drawer class end }//main class end 

你永远不会想要在Event Dispatch Thread上使用Thread.sleep() 。 EVER !!

它永远不会完成任何好事,它会让你的屏幕冻结,直到睡眠结束。 所以不要这样做!!

在Swing中制作动画的最佳方法是使用javax.swing.Timer来触发定期更改绘图属性的事件。 这发生在不同的线程上,因此屏幕保持响应。 您可以将此计时器设置为始终运行,但在圆圈不应移动时不执行任何操作。 这是TimerActionListener的代码

 t = new Timer(TIMER_DELAY, new ActionListener() { @Override public void actionPerformed(ActionEvent e) { if(deltaX == 1 && x >= frame.getWidth() - CIRCLE_WIDTH) deltaX = 0; if(deltaY == 1 && y >= frame.getHeight() - CIRCLE_HEIGHT) deltaY = 0; if(deltaX == -1 && x <= 0) deltaX = 0; if(deltaY == -1 && y <= 0) deltaY = 0; x += deltaX; y += deltaY; if(deltaX != 0 || deltaY != 0) { frame.repaint(); } } }); t.start(); 

然后,您的按钮只需通过设置增量值告诉计时器以正确的方向设置动画,例如

 class movingup implements ActionListener { @Override public void actionPerformed(ActionEvent e) { deltaY = -1; } }// moving up end 

如您所见,我在start方法中创建一个计时器对象,然后通过按下按钮告诉计时器开始动画。 计时器有它自己的,不同的ActionListener (在这里实现为一个匿名类 ),实际上告诉参数改变,然后重新安装,如果有什么东西移动。 您可以通过告诉Swing仅重绘需要使用JComponent.repaint(Rectangle)重绘的屏幕部分来进一步优化它,但这只是一个示例。 如果这样做,请确保重绘大到足以获得圆圈所在的位置和现在的位置。

这是完整的程序,包括常量CIRCLE_WIDTHCIRCLE_HEIGHT ,新的计时器逻辑,以及所有四个按钮都改变了:

 package swingWork; import java.awt.BorderLayout; import java.awt.Button; import java.awt.Color; import java.awt.Graphics; import java.awt.event.ActionEvent; import java.awt.event.ActionListener; import javax.swing.*; public class movingball { private static final int CIRCLE_WIDTH = 100; private static final int CIRCLE_HEIGHT = 100; private static final int TIMER_DELAY = 50; JFrame frame; int x = 0; int y = 0; int deltaX = 0; int deltaY = 0; Timer t; public static void main(String[] args) { movingball ball = new movingball(); ball.start(); } public void start() { ballDrawer drawer = new ballDrawer(); frame = new JFrame(); frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE); JButton up = new JButton("UP"); JButton down = new JButton("DOWN"); JButton left = new JButton("LEFT"); JButton right = new JButton("RIGHT"); frame.getContentPane().add(BorderLayout.NORTH, up); frame.getContentPane().add(BorderLayout.SOUTH, down); frame.getContentPane().add(BorderLayout.WEST, left); frame.getContentPane().add(BorderLayout.EAST, right); frame.getContentPane().add(BorderLayout.CENTER, drawer); // setting up the action listener: up.addActionListener(new movingup()); down.addActionListener(new movingdown()); left.addActionListener(new movingleft()); right.addActionListener(new movingright()); frame.setSize(300, 300); frame.setVisible(true); t = new Timer(TIMER_DELAY, new ActionListener() { @Override public void actionPerformed(ActionEvent e) { if(deltaX == 1 && x == frame.getWidth() - CIRCLE_WIDTH) deltaX = 0; if(deltaY == 1 && y == frame.getHeight() - CIRCLE_HEIGHT) deltaY = 0; if(deltaX == -1 && x == 0) deltaX = 0; if(deltaY == -1 && y == 0) deltaY = 0; x += deltaX; y += deltaY; if(deltaX != 0 || deltaY != 0) { frame.repaint(); } } }); t.start(); }// start method end // here we have the listeners class movingup implements ActionListener { @Override public void actionPerformed(ActionEvent e) { deltaY = -1; } }// moving up end class movingdown implements ActionListener { @Override public void actionPerformed(ActionEvent e) { deltaY = 1; } }// movingdown end class movingleft implements ActionListener { @Override public void actionPerformed(ActionEvent e) { deltaX = -1; } }// moving left end class movingright implements ActionListener { @Override public void actionPerformed(ActionEvent e) { deltaX = 1; } }// moving right end class ballDrawer extends JPanel { @Override public void paintComponent(Graphics g) { super.paintComponent(g); g.setColor(Color.white); g.fillRect(0, 0, this.getWidth(), this.getHeight()); g.setColor(Color.black); g.fillOval(x, y, CIRCLE_WIDTH, CIRCLE_HEIGHT); }// PaintComponents End }// ball drawer class end }// main class endreading: http://stackoverflow.com/q/15511282/1768232 

正如已经指出的那样,你正在阻止事件调度线程,这阻止了UI的更新直到你的循环和方法退出

更好的解决方案是使用Swing Timer ,它在EDT上下文中为ActionListener生成常规回调,允许您从其中安全地更新UI。 有关更多详细信息,请参阅Swing中的并发和如何使用Swing Timers

您还应该调用super.paintComponent ,除其他外,它将自动填充Graphcis上下文的背景。

 import java.awt.BorderLayout; import java.awt.Color; import java.awt.Dimension; import java.awt.EventQueue; import java.awt.Graphics; import java.awt.event.ActionEvent; import java.awt.event.ActionListener; import javax.swing.JButton; import javax.swing.JFrame; import javax.swing.JPanel; import javax.swing.Timer; import javax.swing.UIManager; import javax.swing.UnsupportedLookAndFeelException; public class Movingball { public static void main(String[] args) { EventQueue.invokeLater(new Runnable() { @Override public void run() { try { UIManager.setLookAndFeel(UIManager.getSystemLookAndFeelClassName()); } catch (ClassNotFoundException | InstantiationException | IllegalAccessException | UnsupportedLookAndFeelException ex) { ex.printStackTrace(); } Movingball ball = new Movingball(); ball.start(); } }); } private BallDrawer drawer; public void start() { drawer = new BallDrawer(); JFrame frame = new JFrame(); frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE); JButton up = new JButton("UP"); JButton down = new JButton("DOWN"); JButton left = new JButton("LEFT"); JButton right = new JButton("RIGHT"); frame.getContentPane().add(BorderLayout.NORTH, up); frame.getContentPane().add(BorderLayout.SOUTH, down); frame.getContentPane().add(BorderLayout.WEST, left); frame.getContentPane().add(BorderLayout.EAST, right); frame.getContentPane().add(BorderLayout.CENTER, drawer); // setting up the action listener: up.addActionListener(new Movingup()); down.addActionListener(new Movingdown()); left.addActionListener(new Movingleft()); right.addActionListener(new Movingright()); frame.pack(); frame.setVisible(true); }// start method end // here we have the listeners class Movingup implements ActionListener { @Override public void actionPerformed(ActionEvent e) { drawer.moveUp(); } }// moving up end class Movingdown implements ActionListener { @Override public void actionPerformed(ActionEvent e) { drawer.moveDown(); } }// movingdown end class Movingleft implements ActionListener { @Override public void actionPerformed(ActionEvent e) { drawer.moveLeft(); } }// moving left end class Movingright implements ActionListener { @Override public void actionPerformed(ActionEvent e) { drawer.moveRight(); } }// moving right end public static class BallDrawer extends JPanel { public static final int CIRCLE_WIDTH = 100; public static final int CIRCLE_HEIGHT = 100; private int xDelta; private int yDelta; private int xPos; private int yPos; public BallDrawer() { Timer timer = new Timer(40, new ActionListener() { @Override public void actionPerformed(ActionEvent e) { xPos += xDelta; yPos += yDelta; if (xPos < 0) { xPos = 0; xDelta = 0; } else if (xPos + CIRCLE_WIDTH > getWidth()) { xPos = getWidth() - CIRCLE_WIDTH; xDelta = 0; } if (yPos < 0) { yPos = 0; yDelta = 0; } else if (yPos + CIRCLE_HEIGHT > getHeight()) { yPos = getHeight() - CIRCLE_HEIGHT; yDelta = 0; } repaint(); } }); timer.start(); } @Override public Dimension getPreferredSize() { return new Dimension(400, 400); } public void moveLeft() { xDelta = -1; } public void moveRight() { xDelta = 1; } public void moveUp() { yDelta = -1; } public void moveDown() { yDelta = 1; } @Override protected void paintComponent(Graphics g) { super.paintComponent(g); g.setColor(Color.black); g.fillOval(xPos, yPos, CIRCLE_WIDTH, CIRCLE_HEIGHT); }// PaintComponents End }// ball drawer class end }// main class end 

您还应该将function隔离到负责实际管理它的类中,在这种情况下,您的BallDrawer应该管理球的移动,而不是其他类,这允许它自包含和可重用。

for循环导致很多延迟。 尝试像这样重构你的代码。

 public class MovingBall{ String direction; int x=0; int y=0; public static void main(String[] args){ //create your frame and buttons... upButton.addActionListener(new ActionListener{ direction = "up"; }); //repeat this actionListener to the other buttons while (true){ try{Thread.sleep(50);}catch(Exception e){e.printStackTrace();} drawer.repaint(); } } public class Drawer extends JPanel{ public void paint(Graphics g){ if(direction.equals("up")) { y = y - 5; } //repeat the if statement with the other directions g.setColor(Color.white); g.fillRect(0,0,frame.getWidth(),frame.getHeight()); g.setColor(Color.black); g.fillOval(x,y,100,100); } } }