`
Chang0501
  • 浏览: 23674 次
  • 性别: Icon_minigender_1
  • 来自: 长沙
社区版块
存档分类
最新评论

双缓冲原理在awt和swing中实现消除闪烁的方法

阅读更多

对于双缓冲的分析是在坦克大战游戏的设计时开始的,由于当时忙于游戏的整体设计,所以对这一个问题没有进行详细的研究,现在就这个问题来谈谈自己的一些看法。

 

分析前提出几个问题:

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) {
			// 截取窗体所在位置的图片

 

结果是:

 
 

是不是有点吃惊了,在我没有故意编出这个东西忽悠大伙的前提下我们可以得知,在swingupdate()方法并没有像awtupdate()那样随时被调用,所以就很好解释为什么该为继承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
6
14
分享到:
评论
2 楼 JuliaAilse 2011-10-21  
羡慕的说!!!我要加油努力了!
1 楼 Coco_young 2011-05-04  
相当给力

相关推荐

    三菱PLC例程源码QD75P八轴定位系统程序

    三菱PLC例程源码QD75P八轴定位系统程序本资源系百度网盘分享地址

    WeRoBot-0.3.2-py2.7.egg

    Python库是一组预先编写的代码模块,旨在帮助开发者实现特定的编程任务,无需从零开始编写代码。这些库可以包括各种功能,如数学运算、文件操作、数据分析和网络编程等。Python社区提供了大量的第三方库,如NumPy、Pandas和Requests,极大地丰富了Python的应用领域,从数据科学到Web开发。Python库的丰富性是Python成为最受欢迎的编程语言之一的关键原因之一。这些库不仅为初学者提供了快速入门的途径,而且为经验丰富的开发者提供了强大的工具,以高效率、高质量地完成复杂任务。例如,Matplotlib和Seaborn库在数据可视化领域内非常受欢迎,它们提供了广泛的工具和技术,可以创建高度定制化的图表和图形,帮助数据科学家和分析师在数据探索和结果展示中更有效地传达信息。

    tensorflow_serving_api_gpu-1.14.0-py2.py3-none-any.whl

    Python库是一组预先编写的代码模块,旨在帮助开发者实现特定的编程任务,无需从零开始编写代码。这些库可以包括各种功能,如数学运算、文件操作、数据分析和网络编程等。Python社区提供了大量的第三方库,如NumPy、Pandas和Requests,极大地丰富了Python的应用领域,从数据科学到Web开发。Python库的丰富性是Python成为最受欢迎的编程语言之一的关键原因之一。这些库不仅为初学者提供了快速入门的途径,而且为经验丰富的开发者提供了强大的工具,以高效率、高质量地完成复杂任务。例如,Matplotlib和Seaborn库在数据可视化领域内非常受欢迎,它们提供了广泛的工具和技术,可以创建高度定制化的图表和图形,帮助数据科学家和分析师在数据探索和结果展示中更有效地传达信息。

    三菱PLC例程源码ro1-chunshui

    三菱PLC例程源码ro1_chun shui本资源系百度网盘分享地址

    产品经理与产品原型(详解)

    互联网产品经理在向技术部门递交产品策划方案时,除了详尽的需求阐述,一份清晰易懂的产品原型设计方案同样不可或缺。一份出色的原型设计,不仅能促进前期的深入讨论,更能让美工和开发人员更直观地理解产品特性,进而优化工作流程,减少不必要的时间消耗,提升整体工作效率。接下来,我想就产品经理与原型设计之间的关系进行简要的探讨,并期待能与大家进行深入的交流和探讨。 产品原型,简而言之,是产品设计最终成形之前的一个基础框架。对于网站而言,它意味着对页面模块和元素进行初步、大致的排版与布局。进一步讲,我们还会加入一些交互性元素,使原型更加具体、生动,更贴近最终产品的形态,从而帮助团队成员更好地理解和构建产品。 很多人存在一个误区,认为UI、UE设计师的职责就是将产品原型转化为具体的页面效果。然而,实际上他们的工作流程是在原型设计完成之后展开的。在整个产品开发流程中,产品经理是最了解产品特性、用户和市场需求的角色。尽管设计师在视觉设计方面可能表现得非常出色,但他们在理解产品、用户、市场及业务方面的深度与准确性上,往往无法与产品经理相提并论。准确地说,设计师的工作是将产品原型转化为产品经理所期望的视觉效果。

    小红书娱乐营销520明星大事件《全明星告白季》招商方案ss.pptx

    小红书娱乐营销520明星大事件《全明星告白季》招商方案ss.pptx

    本科毕业设计-基于深度学习的模糊人脸图像增强系统的设计与实现.zip

    人工智能毕业设计&课程设计

    三菱PLC例程源码车辆进出车库

    三菱PLC例程源码车辆进出车库本资源系百度网盘分享地址

    三菱PLC例程源码PLCFX0N三层三站程序

    三菱PLC例程源码PLCFX0N三层三站程序本资源系百度网盘分享地址

    三菱PLC例程源码纯水控制三菱PLC实例和触摸屏程序

    三菱PLC例程源码纯水控制三菱PLC实例和触摸屏程序本资源系百度网盘分享地址

    三菱PLC例程源码pp復卷機三菱伺服編程

    三菱PLC例程源码pp復卷機三菱伺服編程本资源系百度网盘分享地址

    三菱PLC例程源码变频器16段速控制

    三菱PLC例程源码变频器16段速控制本资源系百度网盘分享地址

    wheel-0.34.2.tar.gz

    Python库是一组预先编写的代码模块,旨在帮助开发者实现特定的编程任务,无需从零开始编写代码。这些库可以包括各种功能,如数学运算、文件操作、数据分析和网络编程等。Python社区提供了大量的第三方库,如NumPy、Pandas和Requests,极大地丰富了Python的应用领域,从数据科学到Web开发。Python库的丰富性是Python成为最受欢迎的编程语言之一的关键原因之一。这些库不仅为初学者提供了快速入门的途径,而且为经验丰富的开发者提供了强大的工具,以高效率、高质量地完成复杂任务。例如,Matplotlib和Seaborn库在数据可视化领域内非常受欢迎,它们提供了广泛的工具和技术,可以创建高度定制化的图表和图形,帮助数据科学家和分析师在数据探索和结果展示中更有效地传达信息。

    基于SSM框架的二手物品交易管理系统设计源码

    这是一个基于SSM框架的二手物品交易管理系统设计,使用Java语言开发,包含344个文件。主要文件类型包括59个JPG图片文件、59个Java源文件、48个PNG图片文件、37个JAR包文件、34个JavaScript文件、28个JSP文件、26个CSS文件、16个XML文件和5个GIF图片文件。该项目提供了一个全面的二手物品交易平台,支持用户发布商品、查看闲置、充值账户、查看所有订单和发布求购信息,旨在为用户提供便捷、安全的交易环境。

    tensorflow_privacy-0.2.1-py3-none-any.whl

    Python库是一组预先编写的代码模块,旨在帮助开发者实现特定的编程任务,无需从零开始编写代码。这些库可以包括各种功能,如数学运算、文件操作、数据分析和网络编程等。Python社区提供了大量的第三方库,如NumPy、Pandas和Requests,极大地丰富了Python的应用领域,从数据科学到Web开发。Python库的丰富性是Python成为最受欢迎的编程语言之一的关键原因之一。这些库不仅为初学者提供了快速入门的途径,而且为经验丰富的开发者提供了强大的工具,以高效率、高质量地完成复杂任务。例如,Matplotlib和Seaborn库在数据可视化领域内非常受欢迎,它们提供了广泛的工具和技术,可以创建高度定制化的图表和图形,帮助数据科学家和分析师在数据探索和结果展示中更有效地传达信息。

    使用DS Client在PPT中动态展示分子三维结构.pdf

    使用DS Client在PPT中动态展示分子三维结构

    quartus ii安装教程.docx

    quartus ii安装教程

    基于ssm高校食堂订餐系统.zip

    基于ssm高校食堂订餐系统.zip

    Google-JavaScript-编码规范指南

    Google JavaScript编码规范指南是Google公司推出的一套详尽且实用的JavaScript编程规范,旨在为开发者提供一个清晰、一致的编码标准,以提高代码质量、可读性和可维护性。这套规范不仅涵盖了JavaScript语言的基础语法和常见模式,还针对Google的实际项目需求,提供了一系列最佳实践和建议。 通过遵循Google JavaScript编码规范指南,开发者可以确保自己的代码风格与团队其他成员保持一致,减少因编码习惯不同而引发的沟通成本和潜在错误。规范中详细说明了变量命名、函数定义、注释书写、代码格式化等方面的要求,使得代码更加整洁、易于阅读和理解。 此外,Google JavaScript编码规范指南还强调了代码性能和可维护性的重要性。它提倡使用高效的数据结构和算法,避免不必要的性能开销;同时,也鼓励开发者编写可重用的代码,减少重复劳动,提高开发效率。 对于初学者来说,Google JavaScript编码规范指南也是一本极佳的入门教材。它可以帮助初学者养成良好的编程习惯,掌握JavaScript的核心概念和技巧,为未来的项目开发打下坚实的基础。

    tensorflow_ranking-0.5.3-py2.py3-none-any.whl

    算、文件操作、数据分析和网络编程等。Python社区提供了大量的第三方库,如NumPy、Pandas和Requests,极大地丰富了Python的应用领域,从数据科学到Web开发。Python库的丰富性是Python成为最受欢迎的编程语言之一的关键原因之一。这些库不仅为初学者提供了快速入门的途径,而且为经验丰富的开发者提供了强大的工具,以高效率、高质量地完成复杂任务。例如,Matplotlib和Seaborn库在数据可视化领域内非常受欢迎,它们提供了广泛的工具和技术,可以创建高度定制化的图表和图形,帮助数据科学家和分析师在数据探索和结果展示中更有效地传达信息。

Global site tag (gtag.js) - Google Analytics