Why does the thread freeze my code in my actionListener implementation?

前端 未结 3 2172
既然无缘
既然无缘 2020-12-12 03:07

I am trying to make an oval move smoothly by taping the buttons (up,down,...)and I try to use threads with the try and catch it\'s work inside the class but when I use it he

3条回答
  •  时光取名叫无心
    2020-12-12 04:11

    You never, ever, EVER want to use Thread.sleep() on the Event Dispatch Thread. EVER!!

    It never accomplishes anything good, and it makes your screen freeze until the sleep is over. So don't do it!!

    The best way to animate in Swing is to use a javax.swing.Timer to fire events that change your drawing properties periodically. This happens on a different thread so the screen stays responsive. You can set this timer to run all the time, but have it do nothing when the circle should not be moving. Here's the code for the Timer'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();
    

    Then, your buttons just tell the timer to animate in the correct direction by setting the delta value, e.g.

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

    As you can see, I create a timer object inside start method, and just tell the timer to start animating by pressing the button. The timer has it's own, different ActionListener (implemented as an anonymous class here) that actually tells the parameters to change and then repaints if something has moved. You could optimize it further by telling Swing to only redraw the part of the screen that needs redrawing with JComponent.repaint(Rectangle), but this is just an example. If you do this, make sure the repaint is large enough to get both where the circle was and where it is now.

    Here's the complete program, including the constants CIRCLE_WIDTH and CIRCLE_HEIGHT, the new timer logic, and all four buttons changed:

    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
    

提交回复
热议问题