关于Java中的paint,repaint,update三个方法的关系

几个星期前做了一个坦克大战,里面用到了这三个方法:repaint,update,paint,并且调用的顺序也是那样,当时没想太多,默认接受了,但是昨天看  Applet 时,它有一个绘图周期,里面就包含了这三个方法,所以有必要深入的研究一下,但是自己能力有限,所以摘录一些高人写下的文章,以丰富自己的知识吧,呵呵呵,也希望对需要的人有帮助
1.  先还是看看 Java API 中的介绍吧:updatepublic void update([url=]Graphics[/url] g)     更新组件。
    如果此组件不是轻量级组件,则为了响应对 repaint 的调用,AWT 调用 update 方法。可以假定未清除背景。
    Component 的 update 方法调用此组件的 paint 方法来重绘此组件。为响应对 repaint 的调用而需要其他工作的子类通常重写    此方法。重写此方法的 Component 子类应该调用 super.update(g),或者直接从其 update 方法中调用 paint(g)。
    图形上下文的原点,即它的(0,0)坐标点是此组件的左上角。图形上下文的剪贴区域是此组件的边界矩形。
repaintpublic void repaint()          重绘此组件。    如果此组件是轻量级组件,则此方法会尽快调用此组件的 paint 方法。否则此方法会尽快调用此组件的 update 方法。 paint
public void paint([url=]Graphics[/url] g)    绘制此组件。
出于性能的考虑,首次显示宽度或高度为 0 的 Component 时认为它不需要进行绘制,并且也不需要修复。
应该绘制组件的内容时调用此方法;例如首次显示组件或者组件已损坏并需要修复时。Graphics 参数中的剪贴区矩形设置为需要绘制的区域。重写此方法的 Component 子类不需要调用 super.paint(g)。2. 引文地址:http://jimobit.blog.163.com/blog/static/2832577820100121066802/   网易博客讲得比较详细,但是语言比较生僻难懂,呵呵呵,将就将就,自己理解了就好了,哈哈哈应用程序不应直接调用 paint,而是应该使用 repaint 方法来安排重绘组件repaint -> update -> paint重量级组件:    重写update方法        轻量级组件:    尽快调用paint方法,此方法将绘制工作委托给三个受保护的方法:paintComponent、paintBorder 和 paintChildren。按列出的顺序调用这些方法,以确保子组件出现在组件本身的顶部。    重写paintComponet方法paint(g) ,一个回调方法,绝大多数时候组件的绘图逻辑都存储于这个方法中。awt系统会在适当的时候调用这个方法对组件进行绘图。容器在绘制的时候,容器的paint(g)方法默认会显示通知在容器中存在的所有visible为true的子组件进行绘制. awt调用这个方法有2种形式。程序驱动方式和系统驱动方式。 系统驱动的情况下(比如界面第一次显示组件),系统会判断组件的显示区域,然后向事件分发线程发出调用paint(g)的命令. 程序驱动则由外部的程序调用repaint()方法提出一个异步请求.repaint方法会调用update(g)法,update方法在默认情况下简单调用paint(g)方法.从而绘制组件. 一般不提倡将组件的具体绘制逻辑放到paint(g)之外,也不提倡程序直接调用paint(g)方法.需要注意的是,由于程序驱动方式是由程序来负责组件的绘制,所以repaint方法拥有几个重载方法.传入绘制区域,由程序决定组件的绘制区域. update(g)方法.在awt的重量级组件上,update是一个很重要的方法.在外部程序调用repaint的时候,都会调用到 update.程序员通过继承重量级组件,覆盖这个update方法.可以实现自己的绘图逻辑,典型的用法是重新绘制组件需要改变的的一部份而不是全部 (增量绘图).这样对于复杂的 组件绘制可以提高效率,不然在默认情况下,重量级组件的update将擦除组件的整个区域,并重新调用paint()方法从头绘制.不过轻量级组件不能采用这个方法.这是由于轻量级组件的本质造成的: 重量级组件和轻量级组件的区别,在于前者拥有一个本地同位体,绘制和现实逻辑都是依靠本地系统来完成.而轻量级组件不依赖于任何本地系统.只需要 一个重量级容器组件作为载体.在重量级组件的基础上,完全采用java的绘制代码(个人理解为graphics)来生成.所以轻量级组件在不显示的时候是 透明的.甚至可以认为本组件不存在.由于轻量级组件没有采用任何本地系统代码来进行绘制,所以在需要绘制的情况下需要awt显式的通知,当这个通知来自于 轻量级组件(就是指组件尺寸改变)而非本地系统时(指组件第一次进行绘制),这个通知采用调用repaint方法的形式进行.换句话说,对于轻量级组件, 即使是系统级驱动绘图,也可能会调用repaint方法.而repaint方法将会调用update方法,update方法默认调用paint方法.所以 轻量级组件在系统驱动和程序驱动方式上的绘图并没有什么区别. 即使如此,也有办法对复杂的轻量级组件进行有效的增量绘图.复杂的轻量级组件都可以做成容器.可以根据一些内部信息,只绘制轻量级组件中某几个组件.典型做法是覆盖paint方法. 3.引文地址:百度空间http://hi.baidu.com/yuji0228/blog/item/116917394bea9dc8d462256a.html
[url=http://hi.baidu.com/yuji0228/blog/item/116917394bea9dc8d462256a.html][/url]讲得很好,很实用,易于理解,还有相应错误的原因,很不错,呵呵呵

repaint,update和paint这三个方法在Component中定义,由于awt,swing组件都直接或间接继承自Component,所以几乎所有的awt,swing组件都有这三个方法.这三个方法主要是用于组件的界面绘制.这三个方法执行顺序repaint() - update() - paint()一般在程序中我们都会重装paint()方法以执行重绘画面的动作,但实际上除了paint()以后,update()也执行了一部分的工作,这部分工作包括用默认的背景颜色填充,设置前景色等,由于这部分一般都无须用户参考,所以一般都update()自动完成,然后update()再调用paint()执行用户自定义的绘制操作.
一般情况不用重写update()方法。但是如果你填充背景的颜色跟paint()中的颜色不一样的时候就会有闪烁感。可以重写update方法,事实上,在用双缓冲画图时,一般都会选择重装update()而不是paint()方法,由于在执行paint()方法之前,update()还执行填充背景的动作,所以当重绘的速度很大时,填充背景的动作将很明显,也即闪烁很大,也将,如果在paint()中用双缓冲的话实际上起不了效果.而如果用update()的话就可以双缓冲达到预期的效果.    当在程序中需要重绘操作时,我们一般会用repaint()而不是直接调用update()或者paint(),repaint()将调用update().而在各种awt,swing组件需要自动重新绘制时,程序也一般也会自动调用repaint()方法.

 下列就是本人今天做了欢迎介面时遇到的种种困难和错误,希望放在这里能让大家引以为鉴,不要再犯这样的错误!
 1、不理解paint()方法、repaint()方法、update()方法的区别;
 解决思路:paint()方法是由系统自动随机调用,而update()方法是我们自己人为调用,paint()方法必须要人为指定是什么时候让系统开始调用,但是首先在最开始的时候自己调用一次,所以不安全,我们一般使用update()方法,此方法必须用repaint()方法调用。我们调用paint()方法其实也是由repaint()方法调用update()方法(注:paint()方法在加图之前必须判断内存里面是否有图片);
 2、程序执行顺序问题:this.memoryImage()!=null;
 解决思路:虚拟机是先加载属性文件;
 3、路径错误;
 解决思路:src是源文件,不要和它混在一起,我们要用到其它如图片和音乐的文件要放在一个文件夹下面,且此文件夹是和src同一级别;
 4、找不到图片;
 解决思路:图片都必须是在媒体跟踪器跟踪完之后才能加载,才能找到其的宽和高,不能在媒体跟踪器加载完之前调用图片的宽和高;
 5、图片覆盖;
 解决思路:图片在画进屏幕的时候要有一定的顺序问题,背景放在最下面,一层一层往上放,要不然会发生覆盖问题;
 6、图片不能显示;
 解决思路:我们的图片画在屏幕上但前题是我们要有一个窗体才能画,所以在将内存的图画进屏幕的时候必须将窗体可见性设出:setVisible(true);
 7、未找到图片的问题判断:
 解决思路:如果没有找到图片,那么那个图片点whith及点height打印出来都是-1;

-------------------------------------------------------------------------------------------------关于上面提到的双缓冲问题,一个最好的解决办法就是重写 update 方法。 例如我在坦克大战中写到的,呵呵,不多说,还是附上源码吧,呵呵
@Override
//重写update方法--这个方法主要是为了防止界面闪烁的问题public void update(Graphics g) {if(offScreenImage == null){offScreenImage = this.createImage(GAME_WIDTH, GAME_HEIGHT);  //由窗体创建一个图片}Graphics gOffScreen = offScreenImage.getGraphics();Color c = gOffScreen.getColor();gOffScreen.setColor(Color.GREEN);gOffScreen.fillRect(0, 0, GAME_WIDTH, GAME_HEIGHT);        //用图片的画笔在图片上那个窗体大小的矩形paint(gOffScreen);                 //然后画上坦克gOffScreen.setColor(c);g.drawImage(offScreenImage, 0, 0, null);   //画虚拟图片}
总结:三个方法的调用顺序是没有错的,然后,对于轻量级组件,一般是重写 paint 方法以快速的绘制组件,但是对于重量及组件,由于重新绘制时间长,容易产生闪烁的现象,所以一般是采用重写 update 方法,利用双缓冲图片来解决闪烁的问题。

呵呵呵呵,今天又懂了一点东西,恩,继续加油,路还长着呢!!!

本文转自:http://yinger090807.blog.163.com/blog/static/142728185201122895629507/

posted on 2011-10-13 19:32  Java码界探秘  阅读(378)  评论(0编辑  收藏  举报

导航