【转载】在Java Swing中 实现双缓冲绘图

这篇文章言简意赅把问题讲透了。

https://blog.csdn.net/zhliro/article/details/45789657

全文如下,以防原文遗失。

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

导致屏闪的原因

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

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

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

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

双缓冲原理

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

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

示例

先创建图像缓冲区:

// 图像缓冲区
private Image image;
1
2
在缓冲区中绘制出要显示到窗口中的内容:

/**
* 绘制图形缓冲区内容
*/
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);
}
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
在paint()方法中调用:

/**
* 将图像缓冲区内容绘制到窗体中
*/
@Override
public void paint(Graphics g) {
drawBufferedImage();
g.drawImage(image, 0, 0, this);
}
1
2
3
4
5
6
7
8
其它代码基本不变。

运行效果:

完整源码:http://download.csdn.net/download/zhliro/8709373
————————————————
版权声明:本文为CSDN博主「江湖人称小明」的原创文章,遵循CC 4.0 BY-SA版权协议,转载请附上原文出处链接及本声明。
原文链接:https://blog.csdn.net/zhliro/article/details/45789657

原文地址:https://www.cnblogs.com/heyang78/p/15104192.html