如何使 Java Swing 动画更流畅 [英] How can I make a Java Swing animation smoother

查看:50
本文介绍了如何使 Java Swing 动画更流畅的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我正在用 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...

我变了

  • framesPerSecond60 似乎有帮助.
  • 降低了可打印区域的整体高度
  • 计算变化区域并简单地调用repaint(Rectangle),只传入发生变化的区域.
  • the framesPerSecond to 60 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屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆