我正在尝试编写一个简单的程序:按下屏幕上的“开始”按钮后出现弹跳球并开始弹跳。应按“X”关闭程序。
出于某种原因,它运行得非常慢。球在闪烁,我按下“X”程序关闭后我必须等待很长时间。
以下是代码:
import java.awt.*;
import java.awt.event.*;
import java.awt.geom.*;
import java.util.*;
import javax.swing.*;
public class Bounce
{
public static void main(String[] args)
{
JFrame frame = new BounceFrame();
frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
frame.show();
}
}
class BounceFrame extends JFrame
{
public BounceFrame()
{
setSize(WIDTH, HEIGHT);
setTitle("Bounce");
Container contentPane = getContentPane();
canvas = new BallCanvas();
contentPane.add(canvas, BorderLayout.CENTER);
JPanel buttonPanel = new JPanel();
addButton(buttonPanel, "Start", new ActionListener()
{
public void actionPerformed(ActionEvent evt)
{
addBall();
}
});
contentPane.add(buttonPanel, BorderLayout.SOUTH);
}
public void addButton(Container c, String title, ActionListener listener)
{
JButton button = new JButton(title);
c.add(button);
button.addActionListener(listener);
}
public void addBall()
{
try
{
Ball b = new Ball(canvas);
canvas.add(b);
for (int i = 1; i <= 10000; i++)
{
b.move();
Thread.sleep(10);
}
}
catch (InterruptedException exception)
{
}
}
private BallCanvas canvas;
public static final int WIDTH = 300;
public static final int HEIGHT = 200;
}
class BallCanvas extends JPanel
{
public void add(Ball b)
{
balls.add(b);
}
public void paintComponent(Graphics g)
{
super.paintComponent(g);
Graphics2D g2 = (Graphics2D)g;
for (int i = 0; i < balls.size(); i++)
{
Ball b = (Ball)balls.get(i);
b.draw(g2);
}
}
private ArrayList balls = new ArrayList();
}
class Ball
{
public Ball(Component c) { canvas = c; }
public void draw(Graphics2D g2)
{
g2.fill(new Ellipse2D.Double(x, y, XSIZE, YSIZE));
}
public void move()
{
x += dx;
y += dy;
if (x < 0)
{
x = 0;
dx = -dx;
}
if (x + XSIZE >= canvas.getWidth())
{
x = canvas.getWidth() - XSIZE;
dx = -dx;
}
if (y < 0)
{
y = 0;
dy = -dy;
}
if (y + YSIZE >= canvas.getHeight())
{
y = canvas.getHeight() - YSIZE;
dy = -dy;
}
canvas.paint(canvas.getGraphics());
}
private Component canvas;
private static final int XSIZE = 15;
private static final int YSIZE = 15;
private int x = 0;
private int y = 0;
private int dx = 2;
private int dy = 2;
}
答案 0 :(得分:3)
缓慢来自两个相关问题,一个简单,一个复杂。
paint
与repaint
由Swing调用以绘制组件。 应用程序不应直接调用
paint
,而应使用repaint
方法安排组件重绘。
所以canvas.paint()
末尾的Ball.move
行必须去。
你想打电话
Component.repaint
代替...
但只需用paint
替换repaint
就会发现第二个问题,即可防止球出现。
ActionListener
理想的ActionListener.actionPerformed
方法会更改程序的状态并尽快返回,使用repaint
之类的惰性方法让Swing在最方便的时候安排实际工作。
相比之下,您的程序基本上完成了actionPerformed
方法中的所有内容,包括 all 动画。
更典型的结构是开始一个
javax.swing.Timer
当你的GUI启动时,让它运行
“永远”,
在每个时钟滴答声中更新模拟的状态。
public BounceFrame()
{
// Original code here.
// Then add:
new javax.swing.Timer(
10, // Your timeout from `addBall`.
new ActionListener()
{
public void actionPerformed(final ActionEvent ae)
{
canvas.moveBalls(); // See below for this method.
}
}
).start();
}
在你的情况下,最重要的
(并完全缺失)
国家是
“我们开始了吗?”
位,可以boolean
存储为BallCanvas
。
这是应该完成所有动画的类,因为它还拥有画布和所有球。
BallCanvas
获得一个字段isRunning
:
private boolean isRunning = false; // new field
// Added generic type to `balls` --- see below.
private java.util.List<Ball> balls = new ArrayList<Ball>();
...和一个setter方法:
public void setRunning(boolean state)
{
this.isRunning = state;
}
最后,BallCanvas.moveBalls
是新的
“更新所有的东西”
Timer
调用的方法:
public void moveBalls()
{
if (! this.isRunning)
{
return;
}
for (final Ball b : balls)
{
// Remember, `move` no longer calls `paint`... It just
// updates some numbers.
b.move();
}
// Now that the visible state has changed, ask Swing to
// schedule repainting the panel.
repaint();
}
(注意,在balls
列表上迭代的次数是多少,现在该列表具有适当的泛型类型。
paintComponent
中的循环可以做得很简单。)
现在BounceFrame.addBall
方法很简单:
public void addBall()
{
Ball b = new Ball(canvas);
canvas.add(b);
this.canvas.setRunning(true);
}
通过此设置,每次按空格键都会为模拟添加另一个球。
我能够在我的2006台式机上弹出超过100个球而没有一丝闪烁。
此外,我可以使用“X”按钮或Alt-F4
退出应用程序,两者都没有在原始版本中作出响应。
如果您发现自己需要更多性能 (或者如果你只是想更好地理解Swing绘画是如何工作的), 看到 “Painting in AWT and Swing: 良好的绘画代码是应用程序性能的关键“ 作者:Amy Fowler。
答案 1 :(得分:0)
我建议你使用&#39;定时器&#39;用于运行gameloop的类。它无限运行,你可以随时使用timer.stop()停止它 您也可以相应地设置其速度。
答案 2 :(得分:-2)
您应该像这样更改addBall()
。
public void addBall() {
Ball b = new Ball(canvas);
canvas.add(b);
new Thread(() -> {
for (int i = 1; i <= 10000; i++) {
EventQueue.invokeLater(() -> b.move());
try {
Thread.sleep(10);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}).start();
}