如何使 Java Swing 动画更流畅 [英] How can I make a Java Swing animation smoother
问题描述
我正在用 Java 创建动画幻灯片过渡,它在我当前型号的 MacBook Pro 和我一年前的 iMac 上使用 Java 6、7 和 8 时不稳定.
I'm creating an animated slide transition in Java, and it is choppy on my current model MacBook Pro and on my year-old iMac, on Java 6, 7, and 8.
在 Mac OS X 上,如何让这个动画对用户来说更流畅?
What can I do to make this animation appear smoother to the user on Mac OS X?
import javax.swing.*;
import java.awt.*;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
public class ScratchSpace {
public static void main(String[] args) {
AnimatedPanel panel = new AnimatedPanel();
JFrame frame = new JFrame();
frame.setContentPane(panel);
frame.pack();
frame.setLocationRelativeTo(null);
frame.setVisible(true);
panel.animate();
}
public static class AnimatedPanel extends JPanel {
private float progress = 0.0f; // a number between 0.0 and 1.0
public AnimatedPanel() {
setPreferredSize(new Dimension(800, 600));
setOpaque(true);
}
public void animate() {
final int animationTime = 1000;
int framesPerSecond = 30;
int delay = 1000 / framesPerSecond;
final long start = System.currentTimeMillis();
final Timer timer = new Timer(delay, null);
timer.addActionListener(new ActionListener() {
@Override
public void actionPerformed(ActionEvent e) {
final long now = System.currentTimeMillis();
final long elapsed = now - start;
progress = (float) elapsed / animationTime;
repaint();
if (elapsed >= animationTime) {
timer.stop();
}
}
});
timer.start();
}
@Override
protected void paintComponent(Graphics g) {
Graphics2D g2d = (Graphics2D) g;
int width = getWidth();
int progressWidth = (int) (width * progress);
g2d.setColor(Color.BLUE);
g2d.fillRect(0, 0, progressWidth, getHeight());
g2d.setColor(Color.RED);
g2d.fillRect(progressWidth, 0, width-progressWidth, getHeight());
}
}
}
推荐答案
很大程度上取决于您最终想要实现的目标.
A lot depends on what it is you ultimately want to achieve.
记住,动画是运动的幻觉...
Remember, animation is the illusion of movement...
我变了
framesPerSecond
到60
似乎有帮助.- 降低了可打印区域的整体高度
- 计算变化区域并简单地调用
repaint(Rectangle)
,只传入发生变化的区域.
- the
framesPerSecond
to60
which seems to have helped. - Reduced the overall height of the printable area
- Calculated the area of change and simple called
repaint(Rectangle)
passing in only that area which has changed.
另一个问题是您的动画需要在很短的时间内覆盖很大的区域.增加时间也会使其更平滑(或减少宽度和/或高度)
The other problem is your animation has a large area to cover in a very short period of time. Increasing the amount of time will also make it smoother (or reducing the width and/or height)
import java.awt.Color;
import java.awt.Dimension;
import java.awt.EventQueue;
import java.awt.Graphics;
import java.awt.Graphics2D;
import java.awt.Rectangle;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import javax.swing.JFrame;
import javax.swing.JPanel;
import javax.swing.Timer;
import javax.swing.UIManager;
import javax.swing.UnsupportedLookAndFeelException;
public class ScratchSpace {
public static void main(String[] args) {
EventQueue.invokeLater(new Runnable() {
@Override
public void run() {
try {
UIManager.setLookAndFeel(UIManager.getSystemLookAndFeelClassName());
} catch (ClassNotFoundException | InstantiationException | IllegalAccessException | UnsupportedLookAndFeelException ex) {
}
AnimatedPanel panel = new AnimatedPanel();
JFrame frame = new JFrame();
frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
frame.setContentPane(panel);
frame.pack();
frame.setLocationRelativeTo(null);
frame.setVisible(true);
panel.animate();
}
});
}
public static class AnimatedPanel extends JPanel {
private float progress = 0.0f; // a number between 0.0 and 1.0
public AnimatedPanel() {
setPreferredSize(new Dimension(800, 100));
}
public void animate() {
final int animationTime = 1000;
int framesPerSecond = 60;
int delay = 1000 / framesPerSecond;
final long start = System.currentTimeMillis();
final Timer timer = new Timer(delay, null);
timer.addActionListener(new ActionListener() {
@Override
public void actionPerformed(ActionEvent e) {
final long now = System.currentTimeMillis();
final long elapsed = now - start;
int width = getWidth();
int height = getHeight();
int oldWidth = (int) (width * progress);
progress = (float) elapsed / animationTime;
int newWidth = (int) (width * progress);
repaint(new Rectangle(oldWidth, 0, newWidth - oldWidth, height));
if (elapsed >= animationTime) {
timer.stop();
}
}
});
timer.start();
}
@Override
protected void paintComponent(Graphics g) {
Graphics2D g2d = (Graphics2D) g;
int width = getWidth();
int progressWidth = (int) (width * progress);
g2d.setColor(Color.BLUE);
g2d.fillRect(0, 0, progressWidth, getHeight());
g2d.setColor(Color.RED);
g2d.fillRect(progressWidth, 0, width - progressWidth, getHeight());
}
}
}
或者,您可以生成自己的后备缓冲区并更新它...
Alternativly, you could generate you own backing buffer and update it...
import java.awt.Color;
import java.awt.Dimension;
import java.awt.EventQueue;
import java.awt.Graphics;
import java.awt.Graphics2D;
import java.awt.RenderingHints;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.awt.geom.Rectangle2D;
import java.awt.image.BufferedImage;
import javax.swing.JFrame;
import javax.swing.JPanel;
import javax.swing.Timer;
import javax.swing.UIManager;
import javax.swing.UnsupportedLookAndFeelException;
public class ScratchSpace {
public static void main(String[] args) {
EventQueue.invokeLater(new Runnable() {
@Override
public void run() {
try {
UIManager.setLookAndFeel(UIManager.getSystemLookAndFeelClassName());
} catch (ClassNotFoundException | InstantiationException | IllegalAccessException | UnsupportedLookAndFeelException ex) {
}
AnimatedPanel panel = new AnimatedPanel();
JFrame frame = new JFrame();
frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
frame.setContentPane(panel);
frame.pack();
frame.setLocationRelativeTo(null);
frame.setVisible(true);
panel.animate();
}
});
}
public static class AnimatedPanel extends JPanel {
private float progress = 0.0f; // a number between 0.0 and 1.0
private BufferedImage buffer;
public AnimatedPanel() {
setPreferredSize(new Dimension(800, 600));
// setOpaque(true);
}
public void animate() {
final int animationTime = 1000;
int framesPerSecond = 60;
int delay = 1000 / framesPerSecond;
final long start = System.currentTimeMillis();
final Timer timer = new Timer(delay, null);
timer.addActionListener(new ActionListener() {
@Override
public void actionPerformed(ActionEvent e) {
final long now = System.currentTimeMillis();
final long elapsed = now - start;
int width = getWidth();
progress = (float) elapsed / animationTime;
updateBuffer();
repaint();
if (elapsed >= animationTime) {
timer.stop();
}
}
});
timer.start();
}
@Override
public void invalidate() {
buffer = null;
updateBuffer();
super.invalidate();
}
protected void updateBuffer() {
if (getWidth() > 0 && getHeight() > 0) {
if (buffer == null) {
buffer = new BufferedImage(getWidth(), getHeight(), BufferedImage.TYPE_INT_RGB);
}
Graphics2D g2d = buffer.createGraphics();
g2d.setRenderingHint(RenderingHints.KEY_ALPHA_INTERPOLATION, RenderingHints.VALUE_ALPHA_INTERPOLATION_QUALITY);
g2d.setRenderingHint(RenderingHints.KEY_ANTIALIASING, RenderingHints.VALUE_ANTIALIAS_ON);
g2d.setRenderingHint(RenderingHints.KEY_COLOR_RENDERING, RenderingHints.VALUE_COLOR_RENDER_QUALITY);
g2d.setRenderingHint(RenderingHints.KEY_DITHERING, RenderingHints.VALUE_DITHER_ENABLE);
g2d.setRenderingHint(RenderingHints.KEY_FRACTIONALMETRICS, RenderingHints.VALUE_FRACTIONALMETRICS_ON);
g2d.setRenderingHint(RenderingHints.KEY_INTERPOLATION, RenderingHints.VALUE_INTERPOLATION_BILINEAR);
g2d.setRenderingHint(RenderingHints.KEY_RENDERING, RenderingHints.VALUE_RENDER_QUALITY);
g2d.setRenderingHint(RenderingHints.KEY_STROKE_CONTROL, RenderingHints.VALUE_STROKE_PURE);
int width = getWidth();
int height = getHeight();
float progressWidth = width * progress;
g2d.setColor(Color.BLUE);
g2d.fill(new Rectangle2D.Float(0, 0, progressWidth, height));
g2d.setColor(Color.RED);
g2d.fill(new Rectangle2D.Float(progressWidth, 0, width - progressWidth, height));
g2d.dispose();
}
}
@Override
protected void paintComponent(Graphics g) {
Graphics2D g2d = (Graphics2D) g;
if (buffer != null) {
g2d.drawImage(buffer, 0, 0, this);
}
// int width = getWidth();
// int progressWidth = (int) (width * progress);
// g2d.setColor(Color.BLUE);
// g2d.fillRect(0, 0, progressWidth, getHeight());
// g2d.setColor(Color.RED);
// g2d.fillRect(progressWidth, 0, width - progressWidth, getHeight());
}
}
}
您遇到的主要问题基本上是您尝试绘制的区域对于您想要绘制的时间来说太大了.
The major problem you have is basically the area you are trying to paint is to large for the time you want to paint it.
另一种选择
您可以创建一个 BufferedImage
来表示进度条并随着进度更新而移动.此更新创建了一个兼容的缓冲图像,这将使渲染器更快
You could create a BufferedImage
that represents the progress bar and move as the progress updates. This update creates a compatible buffered image, which should make it faster to renderer
import java.awt.Color;
import java.awt.Dimension;
import java.awt.EventQueue;
import java.awt.Graphics;
import java.awt.Graphics2D;
import java.awt.GraphicsConfiguration;
import java.awt.GraphicsEnvironment;
import java.awt.RenderingHints;
import java.awt.Transparency;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.awt.geom.Rectangle2D;
import java.awt.image.BufferedImage;
import javax.swing.JFrame;
import javax.swing.JPanel;
import javax.swing.Timer;
import javax.swing.UIManager;
import javax.swing.UnsupportedLookAndFeelException;
public class ScratchSpace {
public static void main(String[] args) {
EventQueue.invokeLater(new Runnable() {
@Override
public void run() {
try {
UIManager.setLookAndFeel(UIManager.getSystemLookAndFeelClassName());
} catch (ClassNotFoundException | InstantiationException | IllegalAccessException | UnsupportedLookAndFeelException ex) {
}
AnimatedPanel panel = new AnimatedPanel();
JFrame frame = new JFrame();
frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
frame.setContentPane(panel);
frame.pack();
frame.setLocationRelativeTo(null);
frame.setVisible(true);
panel.animate();
}
});
}
public static class AnimatedPanel extends JPanel {
private float progress = 0.0f; // a number between 0.0 and 1.0
private BufferedImage buffer;
public AnimatedPanel() {
setPreferredSize(new Dimension(800, 600));
// setOpaque(true);
}
public void animate() {
final int animationTime = 1000;
int framesPerSecond = 60;
int delay = 1000 / framesPerSecond;
final long start = System.currentTimeMillis();
final Timer timer = new Timer(delay, null);
timer.addActionListener(new ActionListener() {
@Override
public void actionPerformed(ActionEvent e) {
final long now = System.currentTimeMillis();
final long elapsed = now - start;
progress = (float) elapsed / animationTime;
repaint();
if (elapsed >= animationTime) {
timer.stop();
}
}
});
timer.start();
}
@Override
public void invalidate() {
buffer = null;
updateBuffer();
super.invalidate();
}
protected void updateBuffer() {
if (getWidth() > 0 && getHeight() > 0) {
if (buffer == null) {
GraphicsConfiguration config = GraphicsEnvironment.getLocalGraphicsEnvironment().getDefaultScreenDevice().getDefaultConfiguration();
buffer = config.createCompatibleImage(getWidth(), getHeight(), Transparency.TRANSLUCENT);
buffer.coerceData(true);
Graphics2D g2d = buffer.createGraphics();
g2d.setRenderingHint(RenderingHints.KEY_ALPHA_INTERPOLATION, RenderingHints.VALUE_ALPHA_INTERPOLATION_QUALITY);
g2d.setRenderingHint(RenderingHints.KEY_ANTIALIASING, RenderingHints.VALUE_ANTIALIAS_ON);
g2d.setRenderingHint(RenderingHints.KEY_COLOR_RENDERING, RenderingHints.VALUE_COLOR_RENDER_QUALITY);
g2d.setRenderingHint(RenderingHints.KEY_DITHERING, RenderingHints.VALUE_DITHER_ENABLE);
g2d.setRenderingHint(RenderingHints.KEY_FRACTIONALMETRICS, RenderingHints.VALUE_FRACTIONALMETRICS_ON);
g2d.setRenderingHint(RenderingHints.KEY_INTERPOLATION, RenderingHints.VALUE_INTERPOLATION_BILINEAR);
g2d.setRenderingHint(RenderingHints.KEY_RENDERING, RenderingHints.VALUE_RENDER_QUALITY);
g2d.setRenderingHint(RenderingHints.KEY_STROKE_CONTROL, RenderingHints.VALUE_STROKE_PURE);
int width = getWidth();
int height = getHeight();
g2d.setColor(Color.BLUE);
g2d.fill(new Rectangle2D.Float(0, 0, width, height));
g2d.dispose();
}
}
}
@Override
protected void paintComponent(Graphics g) {
Graphics2D g2d = (Graphics2D) g;
int width = getWidth();
int progressWidth = (int) (width * progress);
int x = (progressWidth - width);
System.out.println(progressWidth + "; " + x);
// g2d.setColor(Color.BLUE);
// g2d.fillRect(0, 0, progressWidth, getHeight());
g2d.setColor(Color.RED);
g2d.fillRect(progressWidth, 0, width - progressWidth, getHeight());
g2d.drawImage(buffer, x, 0, this);
}
}
}
这篇关于如何使 Java Swing 动画更流畅的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!