对于双缓冲的分析是在坦克大战游戏的设计时开始的,由于当时忙于游戏的整体设计,所以对这一个问题没有进行详细的研究,现在就这个问题来谈谈自己的一些看法。
分析前提出几个问题:
1、为什么当想屏幕上添加图片之后会有明显的闪烁现象?
2、在awt中如何实现双缓冲?
3、如何理解swing内置双缓冲以及比较他与awt中消除闪烁的方法区别在哪里?
首先我们来解答第一个问题:
我们在屏幕上自绘图形或者是添加图片都是要通过所在画布的重绘来实现的,因此闪烁的出现必然与重绘机制有着一些关联。在awt中对于窗体画布的重绘其条用顺序是repaint() —>update()—>paint();我们来看看update()的源码:
/**
* Updates the container. This forwards the update to any lightweight
* components that are children of this container. If this method is
* reimplemented, super.update(g) should be called so that lightweight
* components are properly rendered. If a child component is entirely
* clipped by the current clipping setting in g, update() will not be
* forwarded to that child.
*
* @param g the specified Graphics window
* @see Component#update(Graphics)
*/
public void update(Graphics g) {
if (isShowing()) {
if (! (peer instanceof LightweightPeer)) {
g.clearRect(0, 0, width, height);
}
paint(g);
}
}
从这里我们可以清晰的看到,update中有一个清屏的作用,即g.clearRect(0, 0, width, height);然后再在下面调用paint(g),函数进行重绘。因此到这里的话我们可以在一定程度上对底层的重绘机制有一个了解了。
现在我们明白了,屏幕上之所以出现闪烁是因为在update()方法内先要哗哗的清空屏幕上原有的东西,然后又哗哗的往上画,所以在我们需要不断重绘的屏幕上出现闪烁是必然的了,哪怕CPU的速度快之又快。
通过上述的分析,在awt中我们解决闪烁问题的思路也因该随之产生,即重写update()函数的代码,改变它的工作原理。于是我们引进一段在坦克大战中已经重写了的update()方法。其中通过改变重绘函数paint(g)重绘的画布对象,由窗体的画布变为截取的图片上的画布gImage,这样的话就很大程度上改善这个问题了。具体如下
// 重写update方法,先将窗体上的图形画在图片对象上,再一次性显示
public void update(Graphics g) {
if (offScreenImage == null) {
// 截取窗体所在位置的图片
offScreenImage = this.createImage(WIDTH, HEIGHT);
}
// 获得截取图片的画布
Graphics gImage = offScreenImage.getGraphics();
// 获取画布的底色并且使用这种颜色填充画布(默认的颜色为黑色)
Color c = Color.BLACK;
gImage.setColor(c);
gImage.fillRect(0, 0, WIDTH, HEIGHT); // 有清除上一步图像的功能,相当于gImage.clearRect(0, 0, WIDTH, HEIGHT)
// 将截下的图片上的画布传给重绘函数,重绘函数只需要在截图的画布上绘制即可,不必在从底层绘制
paint(gImage);
//将接下来的图片加载到窗体画布上去,才能考到每次画的效果
g.drawImage(offScreenImage, 0, 0, null);
}
其实一言以蔽之就是通过重写update()方法改变重绘函数paint(g)重绘的画布对象g。
以上的讨论我们都是在awt中进行,然后大家就想将继承Frame改为JFrame试试,结果一试就傻眼了,屏幕上居然又是哗哗的闪了,真是辛辛苦苦去改变,一下回到解放前,我们不是在update()中实现双缓冲机制了吗?请看下面的一个对比测试:
(1)在awt中测试update():
// 重写update方法,先将窗体上的图形画在图片对象上,再一次性显示
public void update(Graphics g) {
System.out.println("awt的update()在此...");
if (offScreenImage == null) {
// 截取窗体所在位置的图片
看看结果:
要是没觉得意外的话就继续往下看
在swing中测试update():
// 重写update方法,先将窗体上的图形画在图片对象上,再一次性显示
public void update(Graphics g) {
System.out.println("Swing的update()在此...");
if (offScreenImage == null) {
// 截取窗体所在位置的图片
结果是:
是不是有点吃惊了,在我没有故意编出这个东西忽悠大伙的前提下我们可以得知,在swing中update()方法并没有像awt的update()那样随时被调用,所以就很好解释为什么该为继承JFrame之后屏幕重绘闪烁了。就是你认为自己改写了update()方法就会解决这个问题是一厢情愿的,系统并不买你的帐,调都没去调用呐!
那么怎么通过其他的方法消除swing中的闪烁问题呢,我们此时再回到出发点,双缓冲的核心就是改变paint(g)中的画布,那么好了,我直接在paint(g)函数里实现不就得了,下面再来看这一段代码:
public void paint(Graphics g) {
// 在重绘函数中实现双缓冲机制
offScreenImage = this.createImage(WIDTH, HEIGHT);
// 获得截取图片的画布
gImage = offScreenImage.getGraphics();
// 获取画布的底色并且使用这种颜色填充画布,如果没有填充效果的画,则会出现拖动的效果
gImage.setColor(gImage.getColor());
gImage.fillRect(0, 0, WIDTH, HEIGHT); // 有清楚上一步图像的功能,相当于gImage.clearRect(0, 0, WIDTH, HEIGHT)
// 调用父类的重绘方法,传入的是截取图片上的画布,防止再从最底层来重绘
super.paint(gImage);
// 当游戏没有结束的时候绘出对战双方
if (!getGameOver()) {
// 画出自己的坦克
paintMyTank(gImage);
// 画出自己坦克发射的子弹
paintMyBullet(gImage);
// 画出敌方坦克
paintEnemyTank(gImage);
// 画出敌方坦克发射的子弹
paintEnemyBullet(gImage);
}
// 画出草地
paintGrass(gImage);
// 画出小河
paintRiver(gImage);
// 画出石头
paintStone(gImage);
// 画出各种道具
paintTool(gImage);
// 将接下来的图片加载到窗体画布上去,才能考到每次画的效果
g.drawImage(offScreenImage, 0, 0, null);
}
有一些相似的部分吧,其中最重要的是super.paint(gImage)这句,改变画布在这里,消除闪烁也是在这里!!!
下面我们再探讨最后一个问题,即如何理解swing中内置双缓冲,我们首先从继承体系来看,JFrame->Frame->Window->Container->Component,在Frame中的update()方法是从Container中继承而来的,而JFrame中却重写了update()方法如下
/**
* Just calls <code>paint(g)</code>. This method was overridden to
* prevent an unnecessary call to clear the background.
*
* @param g the Graphics context in which to paint
*/
public void update(Graphics g) {
paint(g);
}
与之前的同名方法相比,这里直接调用了paint()函数而没有clearRect(),也就是清屏的方法,这里他试图不通过清屏来阻止闪烁的发生。这也就是JFrame本身的一种处理方法。
以上是通过自己对双缓冲的一些理解,其中还有很多问题,希望牛人们能够积极指出来,并且一起讨论这个问题。
- 大小: 32.5 KB
- 大小: 10.5 KB
分享到:
相关推荐
这是java中awt和swing是的关系以及区别,可以跟好的分别两者中的关系
对照了解AWT与Swing的区别,能更好地理解GUI
java中awt和swing是的关系以及区别java中awt和swing是的关系以及区别
java 图形编程 awt-swing java 图形编程 awt-swing
awt和swing学习实例,代码加注释。
JAVA图形编程(AWT+SWING),包括AWT和SWING两部分。很不错的学习资料,全面,细致,和大家一起分享。
使用Java AWT和Swing实现的计算器,其中的输入输出框为TextField,其余按钮均为JButton,可以实现加、减、乘、除、取反、求倒运算。
AWT和SWING的介绍以及二者的区别与联系
使用文本方式(控制台输入输出)和使用图形方式(可以使用AWT或者SWING)来实现猜数游戏。 进入程序后,显示文本内容,提示用户输入1-100之间的数字,然后程序对其进行判断,用户根据系统提供的提示信息继续猜,...
主要是总结了awt和swing常用组件中常用方法概况。
1.熟悉Java图形用户界面的设计原理和程序结构; 2.能设计符合问题要求的图形用户界面程序; 3.熟悉Java AWT、swing组件的功能; 4.掌握常用组件的事件接口; 5.会应用AWT、swing组件进行应用程序设计。 二、实验内容...
java awt、Swing实现中国象棋可联机版本采用面向对象思想 java awt、Swing实现中国象棋可联机版本采用面向对象思想 采用面向对象的思路,实现中国象棋可联机版本,适合初学者,以及对面向对象有更深层次理解的...
这个代码实现了一个简单的图形用户界面。...在main()方法中演示了如何使用AWT和Swing来创建和管理图形用户界面。由于使用了容器和布局管理器,因此可以方便地控制组件的位置和大小,从而实现灵活的用户界面设计。
用java 的awt和swing 编辑的计算器,编辑的思路比较好,代码精简。实现计算器四则混合运算。
运用面向对象编程,继承、封装和多态等,Java语言的基本语法,Java IO;多线程编程,网络编程,序列化,反射,程序国际化;基本的界面编程(AWT和Swing)等基础知识。是学习java基础的一个极好案例。
AWT和Swing都是java中的包。 AWT(Abstract Window Toolkit):抽象窗口工具包,早期编写图形界面应用程序的包。 Swing :为解决 AWT 存在的问题而新开发的图形界面包。Swing是对AWT的改良和扩展。
Swing 和 SWT,AWT 技术都是在 Java 图形化界面设计中常用的开发包,在进行 Java GUI 编程时,大家或许经 常徘徊在 SWT,Swing,AWT 之间选择,哪一个更合适自己? 本文简单介绍了三者中各自的功能特点,以及之间的区别...
java图形界面设计awt和swing,这是两个chm文件,体积虽小,但内容绝对不少,值得下载
swing基础学习和awt的区别,swing基础学习和awt的区别swing基础学习和awt的区别swing基础学习和awt的区别
AWT的特点是使用操作系统的原生窗口和组件,因此在不同的操作系统上,界面的外观和行为可能会有所不同。这使得AWT在跨平台兼容性方面存在一些问题。 Swing是在AWT的基础上开发的一套GUI工具包。它提供了一系列的类...