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

时间:2015-08-02 21:21:21

标签: java multithreading swing try-catch actionlistener

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

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

3 个答案:

答案 0 :(得分:4)

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

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

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

的代码
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(在这里实现为anonymous class)实际上告诉参数改变,然后如果某些东西移动则重新绘制。您可以通过告诉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

答案 1 :(得分:3)

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

更好的解决方案是使用Swing Timer,它会在EDT上下文中为ActionListener生成常规回调,允许您从其中安全地更新UI。有关详细信息,请参阅Concurrency in SwingHow to use 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

你还应该将功能与负责实际管理它的类隔离开来,在这个BallDrawer应该管理球的运动,而不是其他类,这允许它自包含和可重复使用

答案 2 :(得分:-2)

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

edate = date(datetime.today().year,  datetime.today().month,  datetime.today().day)