在简单的Java动画中停止闪烁

时间:2013-11-04 18:40:20

标签: java swing timer paint japplet

我有一个简单的小程序,可以沿画布的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(); 
    } 
}

** * ** * **** 没有FLICKER的更新代码 * ** * ** * ***

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作为模板。

3 个答案:

答案 0 :(得分:3)

我总是对这种双重缓冲的概念感到困惑。

这是我的例子,它覆盖了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

测试代码

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

答案 1 :(得分:1)

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

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 - 一种重新绘制图像的预定义区域而不是整个图像的技术。

答案 2 :(得分:1)

问题是,像JApplet这样的顶级容器不是双缓冲的。这意味着当它更新时,屏幕会闪烁,因为每个动作都直接在屏幕上完成。

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

由于Swing组件是双缓冲的,因此在将绘制操作绘制到屏幕之前缓冲绘制操作的结果,使其成为单个操作

请查看Performing Custom Painting了解详情