锁定老帖子 主题:[Java]双缓冲技术
精华帖 (0) :: 良好帖 (1) :: 新手帖 (5) :: 隐藏帖 (0)
|
|
---|---|
作者 | 正文 |
发表时间:2010-03-25
我们很多用Java中的g.drawImage()方法导入图像时,如果我们将当前窗口转变成非当前窗口状态,再从非当前窗口恢复到当前窗口状态,有时,某些绘制好的图像会消失,除非我们重新刷新窗口,显示才会恢复正常。此外,当我们移动窗口或者其他的窗口在上移动的时候,图像会有些闪烁。这是怎么一回事呢?这就要涉及到Canvas中的paint方法的绘图机制了。产生这种现象的主要原因是: 1、由于在显示所绘制的图像时,调用了repaint方法。repaint方法被调用时,需要清除整个背景,然后才调用paint方法显示画面。这样,在清除背景和绘制图像的短暂时间间隔内被用户看见的就是闪烁。 2、由于paint()方法需要进行复杂的计算,图像中包含着多个图形,不同图形的复杂程度及其所需要的绘制时间不同,因此,图像中的各个像素值不能同时产生,使得图形的生成频率低于显示器的刷新频率,从而造成闪烁。 下面两种方法可以明显地消除或减弱闪烁: 1、重载update方法 当AWT接收到Canvas重新绘制的请求时,调用Canvas的update方法。默认情况下,update方法清除Canvas的背景,然后调用paint方法。重载update方法,就可以将以前在paint方法中的绘图代码包含在update方法中,从而避免每次重新绘制时将整个区域清除。 2、双缓冲技术 双缓冲技术在很多动画中被采用。主要原理是创建一幅BufferedImage图像,将每一帧画入图像,然后调用drawImage方法,将整个BufferedImage图像一次画到屏幕上去。这种方法的优点在于大部分绘制是在BufferedImage进行的。将BufferedImage绘制的图像一次绘制到屏幕上。首先通过调用new BufferedImage方法生成合适的缓冲区,然后获得在缓冲区的绘图环境(即Graphics类对象)。 综上所述,我们导入图像的思路是:不直接在paint方法中调用各种绘制方法,而是采用重载update方法及双缓冲技术,生成一个图像的缓冲区,获得该缓冲区中的绘图环境后,将该绘图环境读入内存。paint方法不再负责图像的绘制工作,即paint方法不再装入任何的图像绘制代码。我们在paint方法中,直接调用update方法,在内存缓冲区的绘图环境下进行图像的绘制工作,当所有的图像绘制工作完成后,最后将缓冲区的内容一次性地写入Canvas并在窗口中直接显示出来。这种方法很巧妙地解决了图像丢失和闪烁的问题。 例子代码如下: package org.lyndon.test;
import java.awt.Canvas; import java.awt.Dimension; import java.awt.Graphics; import java.awt.Image; import java.awt.image.BufferedImage;
import javax.swing.ImageIcon;
public class MyCanvas extends Canvas {
private static final long serialVersionUID = 1L;
// 窗体的宽与高
public static final int WIDTH = 480;
public static final int HEIGHT = 480;
private Image screen = createImage(WIDTH, HEIGHT, true);// 双缓冲
private Graphics graphics = screen.getGraphics();
private Image resultImage;
/** * 生成一个BufferImage * 生成一个BufferImage BufferImage是Image的子类,左上角坐标都为 (0, 0) * 第三个参数是代码Image图形类型,分为14种,以位数又分为1,2或4位 * * @param width * @param height * @param flag * @return */ final static public BufferedImage createImage(int width, int height, boolean flag) { if (flag) { return new BufferedImage(width, height, 2); } else { return new BufferedImage(width, height, 1); } }
public MyCanvas() { // 设定初始构造时面板大小 setPreferredSize(new Dimension(WIDTH, HEIGHT)); // 初始导入一张图片 ImageIcon icon = new ImageIcon("image/floor.gif"); resultImage = icon.getImage(); }
// @Override public void update(Graphics g) { graphics.drawImage(resultImage, 0, 0, this); g.drawImage(screen, 0, 0, null);// 最后个参数一定要用null,这样可以防止drawImage调用update方法 g.dispose(); }
public void paint(Graphics g) { update(g);//我们在paint方法中,直接调用update方法 }
} 声明:ITeye文章版权属于作者,受法律保护。没有作者书面许可不得转载。
推荐链接
|
|
返回顶楼 | |
发表时间:2010-03-25
最后修改:2010-03-25
这是awt的双缓冲,如果用的是swing就不需要了,因为swing有自己的双缓冲机制
|
|
返回顶楼 | |
发表时间:2010-03-25
因为本人不太用swing,都是使用awt。
|
|
返回顶楼 | |
发表时间:2010-03-25
思路是这样没错,不过自己做这个双缓冲,呵呵,得考虑更多的交互
|
|
返回顶楼 | |
发表时间:2010-03-25
还该如此改善,请楼上指点一下,谢谢。
|
|
返回顶楼 | |
发表时间:2010-03-25
就我能想到的举个例子: 通常我们在桌面应用中,如果窗口刷新缓慢,但是这时候用户有鼠标键盘操作,这些命令会在窗口刷新完成后被顺序执行
|
|
返回顶楼 | |
发表时间:2010-03-25
这样会造成什么情况呢?不是窗口刷新成后,如果这些操作影响窗口,又接着刷新窗口,应该也没有什么问题啊?
|
|
返回顶楼 | |
发表时间:2010-03-25
说实话,每太看懂。awt没大做过。一会再看一遍。
|
|
返回顶楼 | |
发表时间:2010-03-25
lyndon.lin 写道 这样会造成什么情况呢?不是窗口刷新成后,如果这些操作影响窗口,又接着刷新窗口,应该也没有什么问题啊?
比如鼠标点击,捕捉这个事件的组件是刷新前的还是刷新后的? |
|
返回顶楼 | |
发表时间:2010-03-25
刷新前捕捉操作
|
|
返回顶楼 | |
浏览 13034 次