【问题标题】:Stop flickering in simple Java animation在简单的 Java 动画中停止闪烁
【发布时间】:2013-11-04 18:40:19
【问题描述】:

我有一个简单的小程序,它可以沿画布的 x 轴为矩形设置动画。问题是它闪烁。我试过用谷歌搜索这个问题,但没有找到任何有用或我理解的东西。

我对 Java 比较陌生。

谢谢!

import java.awt.Color;
import java.awt.Graphics;
import java.awt.event.*;
import javax.swing.*; 

public class simpleAnimation extends JApplet implements ActionListener { 
    Timer tm = new Timer(10, this); 
    int x = 0, velX = 2;

    public void actionPerformed(ActionEvent event) { 
        if (x < 0 || x > 550){ 
            velX = -velX; 
        }

        x = x + velX; 
        repaint(); 
    }

    public void paint ( Graphics g ) { 
    super.paint(g); 
    g.setColor(Color.RED); 
    g.fillRect(x, 30, 50, 30); 
    tm.start(); 
    } 
}

**********更新后的代码没有闪烁 em>**********

import java.awt.*;
import java.awt.event.*;
import java.awt.geom.*;
import javax.swing.*;

public class simpleAnimation extends JApplet implements ActionListener  
{ 

     Graphics bufferGraphics; 

     Image offscreen; 

     Dimension dim; 

     int x = 3, velX = 2;

     Timer tm = new Timer(10, this);

     public void init()  
     { 

          dim = getSize(); 

          offscreen = createImage(dim.width,dim.height); 

          bufferGraphics = offscreen.getGraphics(); 
     }

      public void paint(Graphics g)  
     { 

          bufferGraphics.clearRect(0,0,dim.width,dim.width); 

          bufferGraphics.setColor(Color.red); 

          bufferGraphics.fillRect(x,50,50,20); 

          g.drawImage(offscreen,0,0,this); 

          tm.start();   

     }

     public void update(Graphics g) 
     { 

          paint(g); 

     } 

    public void actionPerformed(ActionEvent evt) 
    {   

        if ( x < 0 || x > 550){

            velX = -velX;

        }

        x = x + velX;   

        repaint();

    }

 }

我用这个applet 作为模板。

【问题讨论】:

标签: java swing timer paint japplet


【解决方案1】:

我总是对双缓冲这个概念感到困惑。

这是我的示例,它覆盖了 JApplet 的 paint() 和 JPanel 的 paintComponent(),默认情况下使用双缓冲。

我看不出明显的闪烁有什么不同。

//<applet code="SimpleAnimation.class" width="600" height="300"></applet>
import java.awt.*;
import java.awt.Graphics;
import java.awt.event.*;
import javax.swing.*;

public class SimpleAnimation extends JApplet implements ActionListener {
    Timer tm = new Timer(10, this);
    int x = 0, velX = 2;
    JPanel panel;

    public void init()
    {
        panel = new JPanel()
        {
            @Override
            public Dimension getPreferredSize()
            {
                return new Dimension(50, 100);
            }

            @Override
            protected void paintComponent(Graphics g)
            {
                super.paintComponent(g);
                g.setColor(Color.RED);
                g.fillRect(x, 30, 50, 30);

            }
        };
        add(panel, BorderLayout.SOUTH);

        tm.start();
    }

    public void actionPerformed(ActionEvent event) {
        if (x < 0 || x > 550){
            velX = -velX;
        }

        x = x + velX;
        repaint();
//        panel.repaint();
    }

    public void paint ( Graphics g ) {
    super.paint(g);
    g.setColor(Color.RED);
    g.fillRect(x, 30, 50, 30);
    }
}

我使用以下代码测试代码:appletviewer SimpleAnimation.java

我的双缓冲概念有缺陷,还是我的实现有缺陷,或两者兼而有之?

【讨论】:

  • 底部矩形的动画比视频中显示的要流畅很多。
  • 是的,这就是我要问的。 @this_guy,如果底部动画更平滑,那么您应该使用这种方法进行自定义绘画。这是 Swing 完成的绘画方式(正如 MadProgrammer 已经提到的那样)。您覆盖 paint() 和 update() 的方法是使用 AWT 组件完成的,而不是 Swing 组件。此外,这种方法更简单,因为您不需要管理缓冲图像。
  • 我在你的 youtube 视频上看到了闪烁,但在我的机器上,顶部和底部的动画是相同的,然后比视频更好。我猜是显卡,CPU对动画有影响。
  • 感谢您的帮助。除了docs.oracle.com/javase/tutorial 之外,您知道任何好的(初学者)Java 教程网站吗?我发现很多使用 Google 的网站似乎都已经过时了。
  • 我总是从那个教程开始。
【解决方案2】:

问题是,像JApplet 这样的顶级容器不是双缓冲的。这意味着当它更新时,屏幕会随着每个操作直接在屏幕上进行而闪烁。

相反,您应该创建一个自定义组件,使用类似JPanel 的东西,并覆盖其paintComponent 方法并在那里执行您的自定义绘画操作。

因为 Swing 组件是双缓冲的,所以绘制动作的结果在绘制到屏幕之前会被缓冲,使其成为单个动作

查看Performing Custom Painting了解更多详情

【讨论】:

  • 感谢您的帮助,我非常感谢。
  • 嗯,我看到这显然适用于 OP。我看不出有什么不同。我的代码上有任何 cmets 吗? @this_guy,我想看看你实现的代码,看看我的电脑上是否有什么不同。
  • @camickr 您所做的与 OP 所做的之间的显着区别在于,OP 不能再利用我记得的 Swing API(或至少充分利用)。 ..这需要更多的工作。 OP 的方法是直接渲染接近,这意味着它们控制重绘发生的时间,不像 Swing 是被动渲染 API,根据需要进行更新......
  • 对不起,当我发表评论时,我没有看到更新的代码,我认为 OP 正在使用您的建议在 JPanel 上进行自定义绘画(因为那是您的建议)。当我运行我的示例时,我看不出有什么不同。他们都更好地制作了 OP 在我的答案中提供的视频。我猜是显卡和 CPU 会影响动画?
  • 显卡和底层管道一样会产生影响
【解决方案3】:

你现在正在做的事情是这样的:

public void paint ( Graphics g ) {
  // draw the entire area white
  super.paint(g);
  g.setColor(Color.RED);
  // draw a rectangle at the new position
  g.fillRect(x, 30, 50, 30);
}

因此,在每一步中,您首先清除矩形,然后重新绘制它。因此闪烁 - 矩形下的像素不断从白色变为红色到白色到红色到白色到红色......

现在观察你需要做的最少的绘画是(假设矩形向右移动)这个:

  • 在左侧WHITE上绘制velx像素
  • 在右侧 RED 上绘制 velx 像素

如果你这样做,你的动画会很流畅。

虽然计算可能非常具有挑战性,尤其是当您的形状比正方形更复杂/您的动作更复杂时。这就是双缓冲的用武之地。

使用双缓冲,您可以创建与屏幕大小相同的内存图像。你在那里画你的整个主题。然后,您一次在屏幕上绘制该图像。

这样做时,不会有“整个屏幕都是白色”的中间步骤;因此没有闪烁。但请注意,您最终会重新绘制整个屏幕,而不仅仅是更改的区域,这并不理想。考虑使用clipping - 一种重新绘制图像的预定义区域而不是整个区域的技术。

【讨论】:

  • 不是她/他需要把 JPanel 放在那里(所有绘画到顶级容器(默认情况下)都是由闪烁引起的)并覆盖 paintComponent 并使用 JFrame 而不是 JApplet
  • 感谢您的帮助,我非常感谢。
猜你喜欢
  • 1970-01-01
  • 2013-04-17
  • 1970-01-01
  • 1970-01-01
  • 2010-12-29
  • 2011-08-31
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多