使用按钮启动动画 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);
}
});
}
我想在屏幕上创建几个对象 ("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);
}
});
}