如何使Java Swing动画更流畅

我正在用Java创建一个动画幻灯片过渡,它在我当前的MacBook Pro型号以及我的一年前的iMac上,在Java 6,7和8上都是不稳定的。

我该怎么做才能让这个动画在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()); } } } 

很大程度上取决于你最终想要实现的目标。

请记住,动画是运动的错觉……

我变了

  • framesPerSecond60似乎有所帮助。
  • 降低了可打印区域的整体高度
  • 计算了变化的区域和简单的称为repaint(Rectangle)仅传入已更改的区域。

另一个问题是你的动画有很大的区域可以在很短的时间内完成。 增加时间也会使它更平滑(或减少宽度和/或高度)

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

或者,您可以生成自己的后备缓冲区并更新它…

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

您遇到的主要问题基本上是您要绘制的区域在您想要绘制它时要大。

另一种选择

您可以创建一个代表进度条的BufferedImage ,并随着进度更新而移动。 此更新创建兼容的缓冲图像,这将使渲染器更快

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