提供3000多款全球软件/控件产品
针对软件研发的各个阶段提供专业培训与技术咨询
根据客户需求提供定制化的软件开发服务
全球知名设计软件,显著提升设计质量
打造以经营为中心,实现生产过程透明化管理
帮助企业合理产能分配,提高资源利用率
快速打造数字化生产线,实现全流程追溯
生产过程精准追溯,满足企业合规要求
以六西格玛为理论基础,实现产品质量全数字化管理
通过大屏电子看板,实现车间透明化管理
对设备进行全生命周期管理,提高设备综合利用率
实现设备数据的实时采集与监控
利用数字化技术提升油气勘探的效率和成功率
钻井计划优化、实时监控和风险评估
提供业务洞察与决策支持实现数据驱动决策
转帖|其它|编辑:郝浩|2011-05-12 15:01:45.000|阅读 542 次
概述:对于双缓冲的分析是在坦克大战游戏的设计时开始的,由于当时忙于游戏的整体设计,所以对这一个问题没有进行详细的研究,现在就这个问题来谈谈自己的一些看法。
# 慧都年终大促·界面/图表报表/文档/IDE等千款热门软控件火热促销中 >>
对于双缓冲的分析是在坦克大战游戏的设计时开始的,由于当时忙于游戏的整体设计,所以对这一个问题没有进行详细的研究,现在就这个问题来谈谈自己的一些看法。
分析前提出几个问题:
1、为什么当想屏幕上添加图片之后会有明显的闪烁现象?
2、在awt中如何实现双缓冲?
3、如何理解swing内置双缓冲以及比较他与awt中消除闪烁的方法区别在哪里?
首先我们来解答第一个问题:
我们在屏幕上自绘图形或者是添加图片都是要通过所在画布的重绘来实现的,因此闪烁的出现必然与重绘机制有着一些关联。在awt中对于窗体画布的重绘其条用顺序是repaint() —>update()—>paint();我们来看看update()的源码:
Java代码
/**
* 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,这样的话就很大程度上改善这个问题了。具体如下
Java代码
// 重写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():
Java代码
// 重写update方法,先将窗体上的图形画在图片对象上,再一次性显示
public void update(Graphics g) {
System.out.println("awt的update()在此...");
if (offScreenImage == null) {
// 截取窗体所在位置的图片
看看结果:
要是没觉得意外的话就继续往下看
在swing中测试update():
Java代码
// 重写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)函数里实现不就得了,下面再来看这一段代码:
Java代码
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()方法如下
Java代码
/**
* Just calls paint(g). 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本身的一种处理方法。
以上是通过自己对双缓冲的一些理解,其中还有很多问题,希望牛人们能够积极指出来,并且一起讨论这个问题。
本站文章除注明转载外,均为本站原创或翻译。欢迎任何形式的转载,但请务必注明出处、不得修改原文相关链接,如果存在内容上的异议请邮件反馈至chenjj@pclwef.cn
文章转载自:网络转载面对“数字中国”建设和中国制造2025战略实施的机遇期,中车信息公司紧跟时代的步伐,以“集约化、专业化、标准化、精益化、一体化、平台化”为工作目标,大力推进信息服务、工业软件等核心产品及业务的发展。在慧都3D解决方案的实施下,清软英泰建成了多模型来源的综合轻量化显示平台、实现文件不失真的百倍压缩比、针对模型中的大模型文件,在展示平台上进行流畅展示,提升工作效率,优化了使用体验。
本站的模型资源均免费下载,登录后即可下载。模型仅供学习交流,勿做商业用途。
本站的模型资源均免费下载,登录后即可下载。模型仅供学习交流,勿做商业用途。
本站的模型资源均免费下载,登录后即可下载。模型仅供学习交流,勿做商业用途。
服务电话
重庆/ 023-68661681
华东/ 13452821722
华南/ 18100878085
华北/ 17347785263
客户支持
技术支持咨询服务
服务热线:400-700-1020
邮箱:sales@pclwef.cn
关注我们
地址 : 重庆市九龙坡区火炬大道69号6幢