使用Java实现双缓冲绘图

来源:互联网 发布:伦纳德数据 编辑:程序博客网 时间:2024/04/30 01:14

使用Java实现双缓冲绘图

当我们使用AWT或Swing绘图时,如果绘制的图像刷新太快,会出现屏闪现象,如之前写的俄罗斯方块小游戏,屏闪现象就很明显。虽然这种闪烁不会给程序的效果造成太大的影响,但给程序的使用者造成了些许不便,针对这种现象,我们大都是采取双缓冲的方式来解决的。双缓冲是计算机动画处理中的传统技术,在用其他语言编程时也可以实现。

导致屏闪的原因

拿上一篇文章中的俄罗斯方块来说明。当创建窗体对象后,显示窗口,程序首先自动调用paint(Graphics g)方法,在窗口上绘制方块与积分信息,在绘制方块下落的线程启动后,该线程每隔一定的时间就修改一下窗口中方块的位置,然后调用repaint()方法实现重绘。

在repaint()方法中,会对我们重写的paint(Graphics g)进行调用,而在paint(Graphics g)方法中,首先调用了父类的paint()方法(不调用则在不同时刻绘制的方块会重叠在一起形成一条线),而父类paint()方法实现了对组件的清除,即用背景色填充整个窗体,然后会继续调用重写paint()方法中后继语句实现方块绘制。这样,我们就看到了一个在新的位置绘制的方块,前面的方块都被背景色覆盖掉了,这就一帧一帧切换显示,就实现了运动的动画效果。

正是这种用背景色填充组件再重绘图像导致了闪烁。在两次看到处于不同位置方块的中间时刻,存在一个在短时间内被绘制出来的空白画面(即用背景色填充组件)。但即使时间很短,如果重绘的面积较大的话花去的时间也是很大的,这个时间甚至可以大到足以让闪烁严重到让人无法忍受的地步。

我们知道了产生闪烁的原因,那么就可以有针对性的来解决问题,使用双缓冲是处理这类问题的传统技术。

双缓冲原理

先在内存中分配一个和我们动画窗口一样大的空间(缓冲区),然后利用getGraphics()方法去获得双缓冲画笔,接着利用双缓冲画笔在缓冲区中绘制我们想要的东西,最后将缓冲区一次性的绘制到窗体中显示出来,这样在我门的动画窗口上面显示出来的图像就非常流畅了。

在swing中,组件本身就提供了双缓冲的功能,我们只需要进行简单的方法调用就可以实现组件的双缓冲(重写组件的paintComponent()方法),在awt中却没有提供此功能。

示例

先创建图像缓冲区:

// 图像缓冲区private Image image;

在缓冲区中绘制出要显示到窗口中的内容:

/** * 绘制图形缓冲区内容 */private void drawBufferedImage() {    // 创建缓冲区对象    image = createImage(this.getWidth(), this.getHeight());    // 获取图像上下文对象    Graphics g = image.getGraphics();    g.setColor(getBackground());    g.fillRect(0, 0, image.getWidth(null), image.getHeight(null));    /* 绘制缓冲区内容 */    // 绘制方块    for (int i = 0; i < rows; i++) {        for (int j = 0; j < columns; j++) {            if (map[i][j] == State.ACTIVE) { // 绘制活动块                g.setColor(activeColor);                g.fillRoundRect(j * BLOCK_SIZE, i * BLOCK_SIZE + 25,                        BLOCK_SIZE - 1, BLOCK_SIZE - 1, BLOCK_SIZE / 5,                        BLOCK_SIZE / 5);            } else if (map[i][j] == State.STOPED) { // 绘制静止块                g.setColor(stopedColor);                g.fillRoundRect(j * BLOCK_SIZE, i * BLOCK_SIZE + 25,                        BLOCK_SIZE - 1, BLOCK_SIZE - 1, BLOCK_SIZE / 5,                        BLOCK_SIZE / 5);            }        }    }    /* 打印得分 */    g.setColor(scoreColor);    g.setFont(new Font("Times New Roman", Font.BOLD, 30));    g.drawString("SCORE : " + totalScore, 5, 70);    // 游戏结束,打印结束字符串    if (!isGoingOn) {        g.setColor(Color.RED);        g.setFont(new Font("Times New Roman", Font.BOLD, 40));        g.drawString("GAME OVER !", this.getWidth() / 2 - 140,                this.getHeight() / 2);    }}

在paint()方法中调用:

/** * 将图像缓冲区内容绘制到窗体中 */@Overridepublic void paint(Graphics g) {    drawBufferedImage();    g.drawImage(image, 0, 0, this);}

其它代码基本不变。

运行效果:

双缓冲绘图

完整源码:http://download.csdn.net/download/zhliro/8709373

0 0
原创粉丝点击