简体   繁体   English

如何使Java Swing动画更平滑

[英]How can I make a Java Swing animation smoother

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. 我正在用Java创建动画幻灯片过渡,而在我当前的型号MacBook Pro和一岁的iMac,Java 6、7和8上,它是不稳定的。

What can I do to make this animation appear smoother to the user on Mac OS X? 如何使该动画在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... 请记住,动画是运动的错觉...

I changed 我变了

  • the framesPerSecond to 60 which seems to have helped. framesPerSecond60 ,似乎有所帮助。
  • 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. 计算更改区域,然后简单地将repaint(Rectangle)传递到仅更改的区域。

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. 您遇到的主要问题基本上是您想要绘制的区域在想要绘制的时间上太大。

Another Alternatively 另一种选择

You could create a BufferedImage that represents the progress bar and move as the progress updates. 您可以创建一个代表进度条的BufferedImage ,并随着进度更新而移动。 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);
        }
    }
}

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM