双缓冲原理在Awt和Swing中实现消除闪烁方法总结

最近在做Java版贪吃蛇的入门项目,过程中遇到窗口闪烁的问题总结。

一、AWT组件开发

1、AWT

AWT是抽象窗口工具箱的缩写,它为编写图形用户界面提供了用户接口,通过这个接口就可以继承很多方法,省去了很多工作。AWT还能使应用程序更好地同用户进行交互。

AWT中的容器是一种特殊的组件,他可以包含其他组件,即可以把组件方法容器中。Container类是用来存放其他组件的Component类的子类,Frame类又是Component的子类。Frame类用于创建具有标题栏和边界的窗口。这里通过继承Frame类来建立自己的界面。

二、Swing界面编程

       随着Java的发展,AWT已经渐渐被淘汰,它已经不能适应发展的需要,不能满足开发功能强大的用户界面的需要。这时Swing出现了,它是建立在AWT之上的组件集,在不同的平台上都能保持组件的界面样式,因此得到了非常广泛的应用。

1、Swing组件库

在Swing组件中有许多种组件,它们被封装在JFC中,下面我们会对每一种组件进行详细介绍。Swing包很多,但平常用到的只有javax.swing.*和javax.swing.event.*这两个包,其他的很少用到。

       1)、JFC结构

JFC是Java的基础类,是Java Foundation Classes的缩写形式,封装了一组用于构建图形用户界面的组件和特性。JFC包含了图形用户界面构建中需要用到的顶级容器(Applet、Dialog、Frame)、普通容器(面板、滚动面板、拆分窗格组件、选项卡插U能给个和工具条等)、特殊容器(InternalFrame、Layeredpane、root pane)、基本组件(button , combo box , list , menu , slider , spinner和textfild)等。

       2)、与AWT的区别

最大的区别在于Swing组件的实现与本地实现无关。Swing组件比AWT组件具有更多的功能。例如在Swing中添加了按钮组件和标签组件,通过继承来更改Swing组件的行为和外观,访问技术等。


在游戏中相应的实现即主要窗体用Frame和JFrame来构建。

1.Frame:重量级组件

2.JFrame:轻量级组件

出现问题:

①.窗体调用repaint()方法时闪烁严重

②.窗体设置双缓冲重绘后,DrawImage()进行缩放图片时会失真,Graphics2D设置抗锯齿属性也会失效

Ps:问题到目前为止还没有很好的处理,可能相应的用JPanel进行绘制会解决问题(暂未尝试!)


在Awt中对于窗体画布的重绘其条用顺序是repaint() —>update()—>paint();

默认的upadate()中自带clearRect()方法,即清屏功能,程序运行时我们调用repaint()方法刷新则会造成屏幕刚清空,又继续重新调用paint()方法往窗体上绘制,则就造成闪烁问题!

    public void update(Graphics g) {     
        if (isShowing()) {     
            if (! (peer instanceof LightweightPeer)) {     
                g.clearRect(0, 0, width, height);     
            }     
            paint(g);     
        }     
}    

所以闪烁问题我们需要重写update()方法来实现双缓冲

代码:(以本人贪吃蛇项目为例)

public void update(Graphics g) {


		// 截取窗体所在位置的图片  
                   if (image == null)
			image = this.createImage(this.getWidth(), this.getHeight());
		// 获得截取图片的画布
		Graphics gre = image.getGraphics();
                // 获取画布的底色并且使用这种颜色填充画布(默认的颜色为黑色) 
		gre.setColor(gre.getColor());
                // 有清除上一步图像的功能,相当于gImage.clearRect(0, 0, WIDTH, HEIGHT) 
		gre.fillRect(0, 0, this.getWidth(), this.getHeight());
		// 将截下的图片上的画布传给重绘函数,重绘函数只需要在截图的画布上绘制即可,不必在从底层绘制     
		paint(gre);
	        //将接下来的图片加载到窗体画布上去,才能考到每次画的效果  
		g.drawImage(image, 0, 0, null);
		

	}

而Swing中内置双缓冲,我们首先从继承体系来看,JFrame->Frame->Window->Container->Component,在Frame中的update()方法是从Container中继承而来的,而JFrame中却重写了update()方法如下:

public void update(Graphics g) {     
    paint(g);     
}   

对比之后会发现直接调用了paint()函数而没有clearRect(),试图不通过清屏来阻止闪烁的发生。这也就是JFrame本身的一种处理方法。

但是我们用JFrame编程并且重新update()实现双缓冲后,窗体一样狂闪不停!原因为何?

下面引用一张图来说明:

repaint()方法在重量级组件的时候会调用update方法,在轻量级组件的时候会调用paint方法

即JFrame根本不会去调用update()方法!

解决方法:在Paint()中直接进行双缓冲操作

代码:

if (image == null)
			 
	// 截取窗体所在位置的图片  
                   if (image == null)
			image = this.createImage(this.getWidth(), this.getHeight());
		// 获得截取图片的画布
		Graphics gre = image.getGraphics();
                // 获取画布的底色并且使用这种颜色填充画布(默认的颜色为黑色) 
		gre.setColor(gre.getColor());
                // 有清除上一步图像的功能,相当于gImage.clearRect(0, 0, WIDTH, HEIGHT) 
		gre.fillRect(0, 0, this.getWidth(), this.getHeight());
		//调用父类的重绘方法,传入的是截取图片上的画布,防止再从最底层来重绘      
		 super.paint(imageG );
		 
		//蛇身
		if (snake != null) {
			snake.drawSnake(imageG );
			snake.scoreView(imageG );
		}
                //墙体
		if (wall != null)
			wall.drawWall(imageG );
                //食物(金币)
		if (food != null)
			food.drawFood(imageG );
		
		
		
               //将接下来的图片加载到窗体画布上去,才能考到每次画的效果  
		 g.drawImage(image, 0, 0, null);
	}

其中最重要的是 super.paint(imageG );

这里必须先调用父类Frame的方法刷新屏幕清理上一次repaint画出的图像。

然后可以创建一个线程让程序每隔一段时间后自动调用repaint()方法;

本文参与腾讯云自媒体分享计划,欢迎正在阅读的你也加入,一起分享。

发表于

我来说两句

0 条评论
登录 后参与评论

相关文章

来自专栏郭少华

(第一季)Vue2.0-内部指令

Vue (读音 /vjuː/,类似于 view) 是一套用于构建用户界面的渐进式框架。与其它大型框架不同的是,Vue 被设计为可以自底向上逐层应用。Vue 的核...

12830
来自专栏练小习的专栏

闭合浮动元素

按照CSS规范,浮动元素(floats)会被移出文档流,不会影响到块状盒子的布局而只会影响内联盒子(通常是文本)的排列。因此当其高度超出包含容器时,一般父容器不...

20260
来自专栏阮一峰的网络日志

如何做到 jQuery-free?

jQuery是现在最流行的JavaScript工具库。 据统计,目前全世界57.3%的网站使用它。也就是说,10个网站里面,有6个使用jQuery。如果只考察使...

32740
来自专栏HT

基于 HTML5 Canvas 的 3D 模型列表贴图

少量图片对于我们赋值是没有什么难度,但是如果图片的量大的话,我们肯定希望能很直接地显示在界面上供我们使用,再就是排放的位置等等,这些都需要比较直观的操作,在实际...

316100
来自专栏我杨某人的青春满是悔恨

教你写个图片轮播

这是一个图片轮播的 Demo,上半部分用 CollectionView 实现,没有无限循环效果,下半部分是用 ScrollView 实现的,自动无限轮播。代码地...

17650
来自专栏爆栈之路

【开源】微信小程序、小游戏以及 Web 通用 Canvas 渲染引擎 - Cax

用于分组, group 也可以嵌套 group,父容器的属性会叠加在子属性上, 比如:

893160
来自专栏HT

基于 HTML5 Canvas 的 3D 碰撞检测

这是公司大神写的一个放官网上给用户学习的例子,我一开始真的不知道这是在干嘛,就只是将三个形状图元组合在一起,然后可以同时旋转、放大缩小这个三个图形,点击“Ani...

24850
来自专栏跟着阿笨一起玩NET

winform程序中将控件置于最顶层或最底层的方法

一种方法是在WinForm窗体中使用Controls控件集的SetChildIndex方法,该方法将子控件设定为指定的索引值,其方法原型如下:

54820
来自专栏Windows Community

New UWP Community Toolkit - RotatorTile

概述 UWP Community Toolkit  中有一个为图片或磁贴提供轮播效果的控件 - RotatorTile,本篇我们结合代码详细讲解  Rotato...

32280
来自专栏菩提树下的杨过

silverlight如何在运行时用代码动态控制(或创建)动画

silverlight做一些复杂动画时,不可能所有的动画都事先用Blend之类的设计工具"画"好(或者在设计期就在vs里编好),很多时候我们希望在运行时能动态控...

213100

扫码关注云+社区

领取腾讯云代金券