Cog*_*ent 4 java performance animation swing timer
我有两个几乎相同的类:AnimationFrame1和AnimationFrame2.这两个类都显示一个蓝色的球在500 x 500的窗口上水平来回移动.除了runAnimation()和createAndShowGUI()方法之外,这两个类是相同的.在其runAnimation()方法中,AnimationFrame1使用while循环和sleep方法创建动画循环,而AnimationFrame2使用Swing Timer.在其createAndShowGUI()方法中,AnimationFrame1创建一个新线程并在其上调用runAnimation()方法,而AnimationFrame2只调用没有新线程的runAnimation()方法.
在编译了这两个类之后,我发现使用Swing Timer的AnimationFrame2显示了一个更平滑的动画,它不会像使用while循环和sleep方法的AnimationFrame1中显示的动画那样断断续续.我的问题是:为什么AnimationFrame1在动画中显示出比AnimationFrame2更多的口吃?我一直在寻找原因,但迄今为止一无所获.
另外,我显然是Java新手,所以如果您发现我的代码有任何问题或者您知道我可以改进它的任何方式,请告诉我.
这是AnimationFrame1:
import javax.swing.*;
import java.awt.*;
import java.awt.event.ActionListener;
import java.awt.event.ActionEvent;
import java.awt.image.BufferedImage;
class AnimationFrame1 extends JPanel {
int ovalX;
int prevX;
Timer timer;
boolean moveRight;
BufferedImage img;
public AnimationFrame1() {
setPreferredSize(new Dimension(500, 500));
}
public void runAnimation() {
moveRight = true;
img = null;
ovalX = 0;
prevX = 0;
while(true) {
if (moveRight == true) {
prevX = ovalX;
ovalX = ovalX + 4;
}
else {
prevX = ovalX - 4;
ovalX = ovalX - 4;
}
repaint();
if (ovalX > 430) {
moveRight = false;
}
if (ovalX == 0) {
moveRight = true;
}
try {
Thread.sleep(25);
}
catch(Exception e) {
}
}
}
public void paintComponent(Graphics g) {
if (img == null) {
GraphicsEnvironment ge = GraphicsEnvironment.getLocalGraphicsEnvironment();
GraphicsDevice gs = ge.getDefaultScreenDevice();
GraphicsConfiguration gc = getGraphicsConfiguration();
img = gc.createCompatibleImage(78, 70);
Graphics gImg = img.getGraphics();
gImg.setColor(getBackground());
gImg.fillRect(0, 0, getWidth(), getHeight());
gImg.setColor(Color.BLUE);
gImg.fillOval(4, 0, 70, 70);
gImg.dispose();
}
g.drawImage(img, ovalX, 250, null);
}
public static void createAndShowGUI() {
JFrame mainFrame = new JFrame();
final AnimationFrame1 animFrame = new AnimationFrame1();
mainFrame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
mainFrame.add(animFrame);
mainFrame.pack();
mainFrame.createBufferStrategy(2);
mainFrame.setVisible(true);
new Thread(new Runnable() {
public void run() {
animFrame.runAnimation();
}
}).start();
}
public static void main(String[] args) {
SwingUtilities.invokeLater(new Runnable() {
public void run() {
createAndShowGUI();
}
});
}
}
Run Code Online (Sandbox Code Playgroud)
这是AnimationFrame2:
import javax.swing.*;
import java.awt.*;
import java.awt.event.ActionListener;
import java.awt.event.ActionEvent;
import java.awt.image.BufferedImage;
class AnimationFrame2 extends JPanel {
int ovalX;
int prevX;
Timer timer;
boolean moveRight;
BufferedImage img;
public AnimationFrame2() {
setPreferredSize(new Dimension(500, 500));
}
public void runAnimation() {
moveRight = true;
img = null;
ovalX = 0;
prevX = 0;
timer = new Timer(25, new ActionListener() {
public void actionPerformed(ActionEvent ae) {
if (moveRight == true) {
prevX = ovalX;
ovalX = ovalX + 4;
}
else {
prevX = ovalX - 4;
ovalX = ovalX - 4;
}
repaint();
if (ovalX > 430) {
moveRight = false;
}
if (ovalX == 0) {
moveRight = true;
}
}
});
timer.start();
}
public void paintComponent(Graphics g) {
if (img == null) {
GraphicsEnvironment ge = GraphicsEnvironment.getLocalGraphicsEnvironment();
GraphicsDevice gs = ge.getDefaultScreenDevice();
GraphicsConfiguration gc = getGraphicsConfiguration();
img = gc.createCompatibleImage(78, 70);
Graphics gImg = img.getGraphics();
gImg.setColor(getBackground());
gImg.fillRect(0, 0, getWidth(), getHeight());
gImg.setColor(Color.BLUE);
gImg.fillOval(4, 0, 70, 70);
gImg.dispose();
}
g.drawImage(img, ovalX, 250, null);
}
public static void createAndShowGUI() {
JFrame mainFrame = new JFrame();
final AnimationFrame2 animFrame = new AnimationFrame2();
mainFrame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
mainFrame.add(animFrame);
mainFrame.pack();
mainFrame.createBufferStrategy(2);
mainFrame.setVisible(true);
animFrame.runAnimation();
}
public static void main(String[] args) {
SwingUtilities.invokeLater(new Runnable() {
public void run() {
createAndShowGUI();
}
});
}
}
Run Code Online (Sandbox Code Playgroud)
将标记放入代码后,似乎Timer版本实际上每30毫秒运行一次,而Thread.sleep版本每25毫秒运行一次.可能有几种解释,包括:
如果我将睡眠时间增加到30毫秒,则2个动画类似(实际数量可能因机器而异).
注意:Thread.sleep版本中存在潜在的线程安全问题.您在工作线程和UI线程之间共享变量而没有正确的同步.虽然repaint内部似乎在内部引入了同步障碍,可以确保工作线程从UI线程所做的更改的可见性,但它是一种偶然的效果,并且明确确保可见性是更好的做法,例如通过声明变量volatile .