我想用一个开始按钮在屏幕上创建一些物体(“太空飞船”)的动画。这是我到目前为止的内容:
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;
}
}
很酷的想法是,按下按钮会触发动画。问题在于动画将在不按下开始按钮的情况下自动开始,然后在结束时该按钮无效。我该如何解决?
答案 0 :(得分:1)
让我们从显而易见的开始...
public void paintComponent(Graphics g) {
super.paintComponent(g);
for (Spaceship s : playingList)
s.drawSpaceship(g);
t.start();
}
在t.start
内部调用paintComponent
是一个非常非常非常糟糕的主意。您无法控制绘制过程(即调用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);
}
});
}