使用按钮启动动画 Java Swing

Starting animation with a button Java Swing

我想在屏幕上创建几个对象 ("Spaceships") 的动画,并带有开始按钮。这是我目前所拥有的:

public class SpaceGame extends JPanel implements ActionListener {
    //The list of spaceships that should be painted
    LinkedList<Spaceship> playingList = new LinkedList<Spaceship>(); 
    Timer t = new Timer(5, this);

    @Override
    public void actionPerformed(ActionEvent e) {
        ListIterator<Spaceship> iter = playingList.listIterator();
        while (iter.hasNext()) {
            Spaceship s = iter.next();
            s.moveSpaceship();

        }
        repaint();
    }

    public void paintComponent(Graphics g) {
        super.paintComponent(g);

        for (Spaceship s : playingList)
            s.drawSpaceship(g);
        t.start();
    }

    public static void main(String[] args) {
        SpaceGame game = new SpaceGame();
        JFrame fr = new JFrame();
        fr.setTitle("SPACE GAME");
        fr.setSize(990,690);
        fr.add(game);

        game.playingList .add(new Spaceship(3, 0, 570));
        game.playingList .add(new Spaceship(1, 250, 570));
        game.playingList .add(new Spaceship(2, 500, 570));


        JButton start = new JButton("START");
        start.addActionListener(game);
        fr.add(start,BorderLayout.SOUTH);

        fr.setVisible(true);
        fr.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);

    }

}

其中:

import java.awt.Graphics;

public class Spaceship {
    int initialSpeed;
    int locX, locY; //current location 

    public Spaceship(int initalSpeed, int initX, int initY) {
        this.initialSpeed = initalSpeed;
        locX = initX;
        locY = initY;
    }

    public void drawSpaceship(Graphics g) {
        g.setColor(Color.GREEN);
        g.fillOval(locX, locY, 100, 40);
    }

    public void moveSpaceship() {
        locY -= initialSpeed;
    }


}

当然,这个想法是按下按钮会触发动画。问题是动画会在没有按下开始按钮的情况下自动开始,然后当它结束时,按钮无效。我该如何解决这个问题?

让我们从显而易见的开始...

public void paintComponent(Graphics g) {
    super.paintComponent(g);

    for (Spaceship s : playingList)
        s.drawSpaceship(g);
    t.start();
}

paintComponent 中调用 t.start 是一个非常非常非常糟糕的主意。您无法控制绘制过程(即何时 paintComponent 被调用),因此它可能会出于各种原因随时被调用,通常是快速连续调用。

您应该查看 Painting in AWT and Swing 以了解有关 Swing 绘画工作原理的更多详细信息

绘制应该只绘制组件的当前状态,而不是其他任何东西。

第二个问题是 Timer 和按钮都在调用相同的 actionPerformed 方法,这实际上没有意义。事实上,在一个完美的世界中,你不会像这样直接实现 ActionListener 而是使用 Anonymous Classes 来防止外部 类 直接或间接调用该方法。

那么,解决方案是什么?向您的 SpaceGame 添加一个方法,可以调用它来启动动画,例如...

public class SpaceGame extends JPanel implements ActionListener {
  //The list of spaceships that should be painted

  LinkedList<Spaceship> playingList = new LinkedList<Spaceship>();
  Timer t = new Timer(5, this);

  public void start() {
    if (t.isRunning()) {
      return;
    }
    t.start();
  }

  @Override
  public void actionPerformed(ActionEvent e) {
    ListIterator<Spaceship> iter = playingList.listIterator();
    while (iter.hasNext()) {
      Spaceship s = iter.next();
      s.moveSpaceship();

    }
    repaint();
  }

  public void paintComponent(Graphics g) {
    super.paintComponent(g);

    for (Spaceship s : playingList) {
      s.drawSpaceship(g);
    }
  }

  public static void main(String[] args) {
    EventQueue.invokeLater(new Runnable() {
      @Override
      public void run() {
        SpaceGame game = new SpaceGame();
        JFrame fr = new JFrame();
        fr.setTitle("SPACE GAME");
        // This is unadvisable :/
        fr.setSize(990, 690);
        fr.add(game);

        game.playingList.add(new Spaceship(3, 0, 570));
        game.playingList.add(new Spaceship(1, 250, 570));
        game.playingList.add(new Spaceship(2, 500, 570));

        JButton start = new JButton("START");
        start.addActionListener(new ActionListener() {
          @Override
          public void actionPerformed(java.awt.event.ActionEvent e) {
            game.start();
          }
        });
        fr.add(start, BorderLayout.SOUTH);

        fr.setVisible(true);
        fr.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
      }
    });
  }
}

然后更新您的主要方法以调用它...

public static void main(String[] args) {
  EventQueue.invokeLater(new Runnable() {
    @Override
    public void run() {
      SpaceGame game = new SpaceGame();
      JFrame fr = new JFrame();
      fr.setTitle("SPACE GAME");
      // This is unadvisable :/
      fr.setSize(990, 690);
      fr.add(game);

      game.playingList.add(new Spaceship(3, 0, 570));
      game.playingList.add(new Spaceship(1, 250, 570));
      game.playingList.add(new Spaceship(2, 500, 570));

      JButton start = new JButton("START");
      start.addActionListener(new ActionListener() {
        @Override
        public void actionPerformed(java.awt.event.ActionEvent e) {
          game.start();
        }
      });
      fr.add(start, BorderLayout.SOUTH);

      fr.setVisible(true);
      fr.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
    }
  });
}