gpt4 book ai didi

java - Swing 动画闪烁并导致 GUI 响应缓慢

转载 作者:行者123 更新时间:2023-11-30 06:33:11 25 4
gpt4 key购买 nike

我正在尝试编写一个简单的程序:当您按下屏幕上的“开始”按钮后,会出现一个弹跳球并开始弹跳。应按“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;
}

最佳答案

缓慢来自两个相关的问题,一个简单,一个更复杂。

问题 #1:paintrepaint

来自 JComponent.paint docs :

Invoked by Swing to draw components. Applications should not invoke paint directly, but should instead use the repaint method to schedule the component for redrawing.

因此 Ball.move 末尾的 canvas.paint() 行必须消失。

你想打电话 Component.repaint反而...但只要用 repaint 替换 paint 就会暴露第二个问题,这会导致球甚至无法出现。

问题 #2:在 ActionListener 内设置动画

理想的 ActionListener.actionPerformed 方法会更改程序的状态并尽快返回,使用像 repaint 这样的惰性方法让 Swing 在最需要的时候安排实际工作方便。

相比之下,您的程序基本上会执行 actionPerformed 方法内的所有操作,包括所有动画。

解决方案:A Game Loop

一个更典型的结构是启动一个 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 中。这是应该执行所有动画的类,因为它还拥有 Canvas 和所有球。

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);
}

通过此设置,每次按空格键都会在模拟中添加另一个球。我能够让超过 100 个球在 2006 年的桌面上弹跳,没有任何闪烁的迹象。另外,我可以使用“X”按钮或 Alt-F4 退出应用程序,这两个按钮在原始版本中都没有响应。

如果您发现自己需要更高的性能(或者如果您只是想更好地了解 Swing 绘画的工作原理),看“Painting in AWT and Swing:良好的绘画代码是应用程序性能的关键”作者:艾米·福勒。

关于java - Swing 动画闪烁并导致 GUI 响应缓慢,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/45685332/

25 4 0
Copyright 2021 - 2024 cfsdn All Rights Reserved 蜀ICP备2022000587号
广告合作:1813099741@qq.com 6ren.com