如何使Java Swing动画更流畅

时间:2013-03-19 21:56:54

标签: java swing animation

我正在用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());
        }
    }
}

1 个答案:

答案 0 :(得分:12)

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

请记住,动画就是运动的错觉......

我改变了

  • 似乎有帮助的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);
        }
    }
}