Java动画

我开始对使用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 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(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)  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)  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)  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()); } } } } 

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

仅供参考:最近我想要达到类似的效果: 点击这里

谢谢!

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

nb-在我的脑海里,OP正在从JFrame延伸……

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

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

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

这是一个简单的优化过程的例子 ,我做了500个对象到4500的动画,整体性能只有轻微的降低。

更新

我改变你的代码轻微,它工作正常…

在此处输入图像描述

我将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(); 

要定期更新UI ...