Java动画

时间:2013-09-16 00:05:12

标签: java swing animation

我开始兴趣在Java中制作动画(幻灯片,背景等)。我知道JavaFX这样做要好得多,但我只是顽固地讨厌切换。

这是我到目前为止所得到的。

import java.awt.Color;
import java.awt.GradientPaint;
import java.awt.Graphics;
import java.awt.Graphics2D;
import java.awt.image.BufferedImage;
import java.awt.image.BufferedImageOp;
import java.awt.image.ConvolveOp;
import java.awt.image.Kernel;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Random;

import javax.swing.JFrame;
import javax.swing.JPanel;

public class BlurredLightCells extends JPanel {

    private static final long serialVersionUID = 4610174943257637060L;

    private Random random = new Random();

    private ArrayList<LightCell> lightcells;

    private float[] blurData = new float[500];

    public static void main(String[] args) {
        JFrame frame = new JFrame("Swing animated bubbles");
        frame.setSize(1000, 750);
        frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);

        frame.add(new BlurredLightCells(60));

        frame.setVisible(true);
    }

    public BlurredLightCells(int amtOfBCells) {
        setSize(1000, 750);
        /**
         * Below we initiate all the cells that are going to be drawn on screen
         */
        Arrays.fill(blurData, 1f / 20f);
        lightcells = new ArrayList<LightCell>(amtOfBCells);
        for (int i = 0; i < amtOfBCells; i++) {

            /**
             * Below we generate all the values for each cell(SHOULD be random for each one)
             */
            int baseSpeed = random(0, 3);
            int xSpeed = (int) Math.floor((Math.random() * (baseSpeed - -baseSpeed + baseSpeed)) + -baseSpeed);
            int ySpeed = (int) Math.round((Math.random() * baseSpeed) + 0.5);
            int radius = random(25, 100);
            int x = (int) Math.floor(Math.random() * getWidth());
            int y = (int) Math.floor(Math.random() * getHeight());
            int blurrAmount = (int) (Math.floor(Math.random() * 10) + 5);
            int alpha = (int) ((Math.random() * 15) + 3);

            /**
             * Now we draw a image, and apply transparency and a slight blur to it
             */
            Kernel kernel = new Kernel(blurrAmount, blurrAmount, blurData);
            BufferedImageOp op = new ConvolveOp(kernel);
            BufferedImage circle = new BufferedImage(150, 150, BufferedImage.TYPE_INT_ARGB);
            Graphics2D circlegfx = circle.createGraphics();
            circlegfx.setColor(new Color(255, 255, 255, alpha));
            circlegfx.fillOval(20, 20, radius, radius);
            circle = op.filter(circle, null);
            LightCell bubble = new LightCell(x, y, xSpeed, ySpeed, radius, getDirection(random.nextInt(3)), circle);
            lightcells.add(bubble);
        }
    }

    public int random(int min, int max) {
        final int n = Math.abs(max - min);
        return Math.min(min, max) + (n == 0 ? 0 : random.nextInt(n));
    }

    @Override
    public void paint(Graphics g) {
        int w = getWidth();
        int h = getHeight();
        final Graphics2D g2 = (Graphics2D) g;
        GradientPaint gp = new GradientPaint(-w, -h, Color.LIGHT_GRAY, w, h, Color.DARK_GRAY);
        g2.setPaint(gp);
        g2.fillRect(0, 0, w, h);
        long start = System.currentTimeMillis();
        for (int i = 0; i < lightcells.size(); i++) {
            LightCell cell = lightcells.get(i);
            cell.process(g2);
        }
        System.out.println("Took " + (System.currentTimeMillis() - start) + " milliseconds to draw ALL cells.");
        repaint();
    }

    public String getDirection(int i) {
        switch (i) {
            case 0:
                return "right";
            case 1:
                return "left";
            case 2:
                return "up";
            case 3:
                return "down";
        }
        return "";
    }

    private class LightCell {

        private int x, y, xSpeed, ySpeed, radius;
        private String direction;
        private BufferedImage image;

        public LightCell(int x, int y, int xSpeed, int ySpeed, int radius, String direction, BufferedImage image) {
            this.x = x;
            this.y = y;
            this.xSpeed = xSpeed;
            this.ySpeed = ySpeed;
            this.radius = radius;
            this.direction = direction;
            this.image = image;
        }

        public void process(Graphics g) {
            switch (direction) {
                case "right":
                    moveRight();
                    break;
                case "left":
                    moveLeft();
                    break;
                case "up":
                    moveUp();
                    break;
                case "down":
                    moveDown();
                    break;
            }
            g.drawImage(image, x, y, null);
        }

        private void moveUp() {
            x += xSpeed;
            y -= ySpeed;
            if (y + (radius / 2) < 0) {
                y = getHeight() + (radius / 2);
                x = (int) Math.floor(Math.random() * getWidth());
            }

            if ((x + radius / 2) < 0 || (x - radius / 2) > getWidth()) {
                y = radius + (radius / 2);
                x = (int) Math.floor(Math.random() * getWidth());
            }
        }

        private void moveDown() {
            x += xSpeed;
            y += ySpeed;
            if (y - (radius / 2) > getHeight()) {
                y = 0 - (radius / 2);
                x = (int) Math.floor(Math.random() * getWidth());
            }

            if ((x + radius / 2) < 0 || (x - radius / 2) > getWidth()) {
                y = getHeight() + (radius / 2);
                x = (int) Math.floor(Math.random() * getWidth());
            }
        }

        private void moveRight() {
            x += ySpeed;
            y += xSpeed;
            if (y - (radius / 2) > getHeight() || y + (radius / 2) < 0) {
                x = 0 - (radius / 2);
                y = (int) Math.floor(Math.random() * getHeight());
            }

            if ((x - radius / 2) > getWidth()) {
                x = 0 - (radius / 2);
                y = (int) Math.floor(Math.random() * getWidth());
            }
        }

        private void moveLeft() {
            x -= ySpeed;
            y -= xSpeed;
            if (y - (radius / 2) > getHeight() || y + (radius / 2) < 0) {
                x = getWidth() + (radius / 2);
                y = (int) Math.floor(Math.random() * getHeight());
            }

            if ((x + radius / 2) < 0) {
                x = getWidth() + (radius / 2);
                y = (int) Math.floor(Math.random() * getWidth());
            }
        }
    }
}

如果你运行它,你会看到单元格以非常高的速度移动,如果你查看代码,你会看到我在我覆盖的repaint()方法中调用paint。我知道那不好做。但我的问题是,他是否可以在我现在的repaint()循环之外绘制每个单元格的任何其他方式,因为当我在JFrame中使用其他组件时,这会导致其他组件闪烁/闪烁

仅供参考:最近我想要达到类似的目的:Click Here

谢谢!

1 个答案:

答案 0 :(得分:6)

闪烁的问题与顶级容器不是双缓冲的事实有关。您应该考虑使用更像JFrame的内容并覆盖JPanel,而不是从paintComponent(或其他顶级容器)扩展。

nb-在我的脑海里,OP正在从JFrame ...

延伸出来

两个问题可能导致闪烁。第一个是覆盖paint,第二个是不调用super.paint(g)以及更新之间的时间。更好的解决方案是覆盖paintComponent并确保调用super.paintComponent。使用类似javax.swing.Timer之类的东西来安排更新和定期间隔也会有所帮助......

如果您希望鼓励repaint更新您的组件,请仅致电RepaintManager。不要在任何repaint方法中调用paintXxx,这将导致一个永无止境的绘制请求循环调度到事件队列,最终消耗你的CPU

我会避免在paintXxx方法中执行任何可能需要时间执行的操作,这会降低渲染过程的速度。相反,我会使用javax.swing.Timer进行简单更新或进行更复杂的处理,Thread可用于在模型渲染到屏幕之前更新模型。

这是一个简单的优化过程的example,我将500个对象的动画设置为4500,整体性能只有轻微的降低。

<强>更新

我改变了你的代码,它工作正常......

enter image description here

我将您的paint方法更改为paintComponent并添加了super.paintComponent(g)

@Override
protected void paintComponent(Graphics g) {
    super.paintComponent(g);
    int w = getWidth();
    int h = getHeight();
    final Graphics2D g2 = (Graphics2D) g;
    GradientPaint gp = new GradientPaint(-w, -h, Color.LIGHT_GRAY, w, h, Color.DARK_GRAY);
    g2.setPaint(gp);
    g2.fillRect(0, 0, w, h);
    for (int i = 0; i < lightcells.size(); i++) {
        LightCell cell = lightcells.get(i);
        cell.process(g2);
    }
}

在构造函数结束时,我添加了......

Timer timer = new Timer(40, new ActionListener() {
    @Override
    public void actionPerformed(ActionEvent e) {
        repaint();
    }
});
timer.start();

定期更新用户界面...