关于JavaGUI中的频闪问题解决
今天参照B站的视频课 IT黑马javaGui教学里的p37-p40写了一个简单的桌面绘画程序,照着敲了一遍,基本功能没有问题的。但是频闪问题很头疼,有强迫症,有点不甘心。老师没交代,只好学生自己解决了,毕竟讲究面向百度,面向CSDN的年代了。
先附上我自己实现的原来代码:
package Component;
import java.awt.*;
import java.awt.event.*;
import java.awt.image.BufferedImage;
/**
* 位图案例:绘画板块
* 设计步骤:
* 1.右键弹出框,选择三原色(红、绿、蓝)
* 2.设置BufferedImage作为图片,画图即是不断修改图片。将图片画出在Canvas上即可。
* 3.解析绘图逻辑,利用鼠标拖动事件,传入拖动前位置与本次位置进行绘图
*
* @author yanjq
**/
public class DrawDemo2 {
String className = Thread.currentThread().getStackTrace()[1].getClassName();
Frame frame = new Frame(className.split("\\.")[1]);
private final int AREA_WIDTH = 500;
private final int AREA_HEIGHT = 400;
private final String RED = "红色";
private final String GREEN = "绿色";
private final String BLUE = "蓝色";
PopupMenu popupMenu = new PopupMenu();
MenuItem colorRedMI = new MenuItem(RED);
MenuItem colorGreenMI = new MenuItem(GREEN);
MenuItem colorBlueMI = new MenuItem(BLUE);
private Color nowColor = Color.BLACK;//记录当前颜色
private int preX = -1;//记录上一次鼠标的位置X,用于绘图
private int preY = -1;//记录上一次鼠标的位置Y,用于绘图
//注意传入的最后一个参数
BufferedImage bufferedImage = new BufferedImage(AREA_WIDTH, AREA_HEIGHT, BufferedImage.TYPE_INT_RGB);
//获取画笔
Graphics g = bufferedImage.getGraphics();
MyCanvas myCanvas = new MyCanvas();
private class MyCanvas extends Canvas {
@Override
public void paint(Graphics g) {
//参数理解:1.需要绘制的图片,绘制的起点,观察者暂未使用
g.drawImage(bufferedImage, 0, 0, null);
}
}
private void insertListener() {
//1.菜单项目事件
ActionListener actionListener = e -> {
switch (e.getActionCommand()) {
case RED:
nowColor = Color.RED;
break;
case GREEN:
nowColor = Color.GREEN;
break;
case BLUE:
nowColor = Color.BLUE;
break;
}
};
colorRedMI.addActionListener(actionListener);
colorGreenMI.addActionListener(actionListener);
colorBlueMI.addActionListener(actionListener);
myCanvas.addMouseListener(new MouseAdapter() {
@Override
public void mouseReleased(MouseEvent e) {//鼠标抬起事件
if (e.isPopupTrigger()) { //2.鼠标右击菜单事件
popupMenu.show(myCanvas, e.getX(), e.getY());
}
//重置鼠标初始位置
preX = -1;
preY = -1;
}
});
//3.鼠标拖动事件
myCanvas.addMouseMotionListener(new MouseMotionAdapter() {
@Override
public void mouseDragged(MouseEvent e) {//鼠标拖动事件
//设置画笔颜色,进行划线
if (preX > 0 && preY > 0) {
g.setColor(nowColor);
g.drawLine(preX, preY, e.getX(), e.getY());
}
//若未有记录,则记录当前位置p
preX = e.getX();
preY = e.getY();
myCanvas.repaint();
}
});
}
private void init() {
insertListener();
//组装视图
popupMenu.add(colorRedMI);
popupMenu.add(colorGreenMI);
popupMenu.add(colorBlueMI);
//设置画板初始颜色,若未设置,则默认背景为黑色(#000)
// g.setColor(Color.WHITE);
// g.fillRect(0, 0, AREA_WIDTH, AREA_HEIGHT);
myCanvas.add(popupMenu);
myCanvas.setPreferredSize(new Dimension(AREA_WIDTH, AREA_HEIGHT));
frame.add(myCanvas);
frame.pack();
frame.setVisible(true);
}
public static void main(String[] args) {
new DrawDemo2().init();
}
}
基本功能没有太大毛病,但是频闪很严重,这是为什么呢?
大概看了大佬之前的研究结果,参考的以下两篇博客
再参照老师之前课程里的教学,自己截图的笔记(调用关系图):
猜想大概是repaint()方法里调用的update自带的清屏所致。
下面做简单调试:
在类中的repaint()处以及Canvas中的updte()处做断点。
全黑的效果明显一点。
运行程序:
选择红色画笔,在画布上拖动鼠标,repaint()处F7步入,发现跳转到Canvas中的update()方法
public void update(Graphics g) {
g.clearRect(0, 0, width, height);
paint(g);
}
F8执行clearRect()方法后,屏幕出现白屏
然后再F8执行paint(),屏幕瞬间全黑,并出现画线。
因此大概猜到原因了,是因为Canvas中update调用了clearRect(0, 0, width, height)导致的闪烁。我也不明白设计的初衷是什么。
既然知道问题原因了,那么尝试解决,就是在MyCanvas中重写update。
修改代码如下:
private class MyCanvas extends Canvas {
@Override
public void paint(Graphics g) {
//参数理解:1.需要绘制的图片,绘制的起点,观察者暂未使用
g.drawImage(bufferedImage, 0, 0, null);
}
//重写update,防止闪烁
@Override
public void update(Graphics g) {
paint(g);
}
}
重新运行程序,一发入魂,屏幕闪烁问题解决了。清清爽爽。
第一次写博客,算不上自己的研究成果,但是问题解决了,而且感觉自己去源码中调试程序很有收获,也算锻炼了自己解决问题的思路吧。