在Java图形编程中,经常会遇到需要在窗口中显示动画的情况。如果曾经尝试过,可能已经意识到,直接在屏幕上绘制图形会导致屏幕闪烁和动画不连贯的问题。本文将介绍如何使用双缓冲技术来解决这个问题。
双缓冲是一种在后台缓冲区绘制图形,然后一次性将缓冲区内容绘制到屏幕上的技术。这样做可以避免在绘制过程中屏幕内容被部分更新,从而减少或消除闪烁现象。
在Swing框架中,实现双缓冲非常简单,只需设置一个属性为true即可。Swing框架会自动处理所有必要的任务,不需要关心背后的实现细节。然而,如果使用的是标准的AWT,情况就不同了。AWT没有内置的双缓冲支持,因此需要手动实现。
以下是实现双缓冲的步骤:
首先,创建一个基础类来实现双缓冲功能。可以选择继承Component、Container、Canvas或Panel类,但建议选择Panel类,因为其他类可能会导致事件监听器无法正确触发。
public class DoubleBuffer extends Panel {
public DoubleBuffer() {
super();
}
}
闪烁的原因是调用repaint方法时,VM会尽快重绘组件,但无法确定何时重绘。当VM有时间执行绘制任务时,它会调用update方法。update方法会清除面板,以便可以绘制,而不用担心背景。这就是导致闪烁的原因。需要重写这个方法。
public void update(Graphics g) {
paint(g);
}
public void paint(Graphics g) {
}
双缓冲的核心思想是先在离屏图像上绘制所有内容,然后再一次性绘制到屏幕上。为此,需要一个缓冲图像和它的Graphics对象。这个图像的尺寸始终与面板相同,如果面板大小改变,图像也需要相应调整大小。
private int bufferWidth;
private int bufferHeight;
private Image bufferImage;
private Graphics bufferGraphics;
public void paint(Graphics g) {
if (bufferWidth != getSize().width || bufferHeight != getSize().height || bufferImage == null || bufferGraphics == null) {
resetBuffer();
}
if (bufferGraphics != null) {
bufferGraphics.clearRect(0, 0, bufferWidth, bufferHeight);
paintBuffer(bufferGraphics);
g.drawImage(bufferImage, 0, 0, this);
}
}
private void resetBuffer() {
bufferWidth = getSize().width;
bufferHeight = getSize().height;
if (bufferGraphics != null) {
bufferGraphics.dispose();
bufferGraphics = null;
}
if (bufferImage != null) {
bufferImage.flush();
bufferImage = null;
}
System.gc();
bufferImage = createImage(bufferWidth, bufferHeight);
bufferGraphics = bufferImage.getGraphics();
}
public class MyBuffer extends DoubleBuffer {
private int posX;
public MyBuffer() {
super();
posX = 0;
}
public void animateToTheRight() {
posX++;
repaint();
}
public void paintBuffer(Graphics g) {
g.drawString("Double Buffer!", posX, 20);
}
}