AWT/Swing中的Repaint, paint, update

1.    Core java ch 14 confusion

读Core java 卷I,14.1节的时候,有一段用于在UI上面移动小球的代码,用了paint方法,而不是我们平时用的比较多的repaint,而且jdk文档中明确注明了不推荐直接调用paint,所以有些困惑。


对此,书中的解释是:But if you try to call comp.repaint()in this program, you’ll find that the panel is only

repainted after the addBall method has returned. 

2.    Testing, repaint with paint

查了相关文档,发现paint和repaint的区别还是很大的。Paint方法一般在UI事件的callback中被调用,以进行组件的绘制。如果直接调用paint,绘制将发生在AWT/Swing的事件机制之外。而repaint会向AWT注册一个异步请求,告诉AWT哪个组件需要绘制。这个请求将触发组件的update方法。如果对于同一个组件,在第一个repaint被处理之前,有多个repaint被调用,那么这多个请求将合并为一个请求。

在实例代码中,addBall方法是在事件callback函数(event->addBall())中调用的,在按钮按下触发的callback函数开始之后,addBall才会执行。如果这里用repaint,那么在callback函数(event->addBall())执行结束之后,所有的由repaint注册的update(其实就是paint)方法才会执行,根据前面的规则,所有的repaint请求被合并了。因此,如果用repaint,将会看到球直接移动到了最终位置。

把方法改成repaint,在repaint方法和paint方法中打出log,


看到输出结果:

==============  repaint

==============  repaint

==============  repaint

================== paint


3.    AWT/Swing的历史

Java1.0 刚出现的时候,包含了一个用于基本的UI设计的包,名叫AWT (Abstract Window Toolkit)。AWT将绘制UI的任务委托给了目标平台自带的GUI绘制工具箱。但是AWT不能很好的处理平台之间的差异。

1996年Netscape创建了IFC(Internet Foundation Class),采取了与AWT完全不同的工作方式。在这种工作方式下,目标平台的GUI绘制工具箱负责创建和绘制窗口,而像按钮、菜单这样的用户界面元素由IFC绘制到窗口上。

之后,Sun和Netscape合作,完善了IFC,创建了Swing。Swing作为java1.1的扩展部分使用,并最终在java1.2成为了标准库的一部分。

从Swing的实现方式来说,Swing没有完全替代AWT,而是基于AWT架构之上。Swing提供了能力更为强大的用户界面组件,而且需要使用AWT的事件处理机制。也就是说,不管是AWT还是Swing,UI的绘制都依赖于callback机制:

a.    监听器对象是一个实现了特定监听器接口的类的实例。

b.    事件源(例如按钮)是一个能够注册监听器对象并发送事件的对象。

c.    当事件(例如点击按钮)发生时,事件源将事件对象传递给所有注册的监听器。

d.    监听器对象将对事件作出响应(执行callback函数)

AWT和Swing依赖于组件的paint方法绘制UI。这里需要注意的是,一般情况下,应该避免在callback之外绘制UI,因为这样做跳出了AWT/Swing的事件机制,很容易引起UI绘制中的混乱。因此不推荐直接调用paint。

4.    Lightweight vs heavyweight

从应用开发者的角度来说,二者基本上没有区别:他们都是调用API paint对组件进行绘制。然而,lightweight完全是用java code 开发的,所以在实现机制上还是有细微的差别。因为Lightweight组件必须绘制在heavyweight组件上,当heavyweight组件被告知需要调用paint的时候,它应该将绘制信息传递给其所有的子lightweight组件。而且,lightweight具有透明性,着意味着update不会清空lightweight的背景(这一点有别于heavyweight组件)。关于update方法的详细信息将在稍后阐述。

a)      Lightweight: 重用 heavyweight组件。

b)      Heavyweight: opaque, native windows.

5.    System trigger vs application trigger

系统触发的事件和应用触发的时间最终要调用组件的paint方法,所以说二者没有本质区别。

a)      System trigger:新建组件,resize,从“破损”的状态下“修复”(没有显示不完全à显示完全)。

 i.           Things with AWT

    1.       AWT决定需要绘制的部分

    2.       AWT导致事件分发线程触法组件的paint方法

b)      App triggered: 由用户动作触发的事件,导致UI的变化。在这种情况下,可以通过调用组件的repaint方法绘制组件。如果组件较为简单,可以调用repaint的无参数版本,以绘制整个组件;反之,如果组件较为复杂,则可以调用repaint的有参数版本,从而仅仅绘制需要的部分。为了响应状态的变化(MVC模型),程序决定绘制组件的部分还是全部。

 i.           Things in AWT

    1.      程序调用组件的repaint方法,该方法向AWT注册一个异步请求,告诉AWT哪个组件需要绘制。这个请求将触发组件的update方法。如果对于同一个组件,在第一个repaint被处理之前,有多个repaint被调用,那么这多个请求将合并为一个请求。

    2.       对于heavyweight组件,默认情况下,Update清空组件需要更新的部分,然后调用组件的paint方法,Update方法支持所谓的“增量绘制”。而对于lightweight组件,Update直接调用paint。因此对于lightweight组件来说,update和paint没有区别。

6.    AWT vs Swing

从Swing的实现方式来说,Swing没有完全替代AWT,而是基于AWT架构之上,并对AWT进行了增强。增强的特性包括:

a.    Double Buffer Support: 这是Swing区别于AWT的最显著特性,该特性为每一个container hierarchy提供了唯一个offscreen(幕后,区别于onscreen) Double Buffer。如果这个特性被enabled(默认情况下enable),那么graphics对象将被转成offscreen graphics,接下来,绘制过程在double buffer中进行,绘制结束后,offscreen graphics会被原始的graphics对象拷贝回组件。

b.   Additional Paint Properties:Swing增加了一些属性,以提高内部绘制算法的效率。这些属性用于描述两类问题:

    a)      透明性,在绘制lightweight组件的时候,可能不会绘制出组件的所有像素,所以必须从container hierarchy向上追溯到第一个heavyweight组件,最后进行“back-to-front”的绘制操作。

        i.           opacity:如果为true,组件同意绘制自己的全部像素。对于大多数组件,opacity为true。

     b)      重叠组件:lightweight组件之间可能相互重叠。

        i.           [endif]IsOptimizedDrawingEnabled:如果为true,表示没有任何直接子组件是重叠的。通过检查这个属性,Swing repaint可以很容易的查找组件之间的重叠关系。

c.    UI Delegate: 绝大多数Swing组件的绘制被代理给UI Delegate(separated Look-and-feel 对象)

d.    RepaintManager:用于最大化Swing组件的绘制效率。

e.    Swing的绘制机制和AWT类似,但是paint方法的实现是不一样的。

   a)      Paint依次调用三个方法,paintComponet-->paintBorder -->paintChild

   b)      如果绘制请求来自于顶层heavyweight对象(JFrame,JDialog,JWindow,JApplet),事件分发线程调用该heavyweight对象的paint方法:

            i.           如果double Butter 特性被enabled,将graphics对象转为offscreen graphics对象。

            ii.          调用paintCompnent

            iii.          调用paintBorder

            iv.           调用paintChildren (传入属性值,opacity,isOptimizedDrawingEnabled)

            v.           如果double Butter 特性被enabled,将offcreen graphics转为原始的graphics对象

     c)       如果绘制请求来自来自于对repaint的调用

              i.           repaint在组件的RepainManager 上注册一个repaint请求,repaintManger用invokeLater将一个runnable(用于稍后处理请求)放入事件分发线程。

              ii.           当runnable运行的时候,会使得repaintManager调用组件的paintImmediately 方法。PaintImmediately方法类似updtate方法。在Swing中,永远不会调用update方法。

               iii.           利用clip、opacity和isOptimizedDrawingEnabled信息,决定从哪个根组件开始绘制。

               iv.           转到b)

Reference

https://docs.oracle.com/javase/7/docs/api/java/awt/Component.html

https://www.oracle.com/technetwork/java/painting-140037.html#triggers

©著作权归作者所有,转载或内容合作请联系作者
【社区内容提示】社区部分内容疑似由AI辅助生成,浏览时请结合常识与多方信息审慎甄别。
平台声明:文章内容(如有图片或视频亦包括在内)由作者上传并发布,文章内容仅代表作者本人观点,简书系信息发布平台,仅提供信息存储服务。

相关阅读更多精彩内容

  • 参考文献:《Java疯狂讲义》(第三版) 绝对定位 绝对定位步骤: 1、Container的布局管理器设成null...
    houc阅读 2,790评论 0 0
  • 一、通用篇1.1 不用new关键词创建类的实例1.2 使用非阻塞I/O1.3 慎用异常1.4 不要重复初始化变量1...
    rebooted阅读 2,586评论 0 1
  • 个人笔记,方便自己查阅使用 Contents Java LangAssignment, ReferenceData...
    freenik阅读 5,292评论 0 6
  • 待更新 概述 在Java初期,包含了一个用于基本GUI程序设计的类库,称为AWT (Abstract Window...
    染微言阅读 4,712评论 0 4
  • https://segmentfault.com/a/1190000000732617http://yanhaij...
    butterflyq阅读 1,657评论 0 0

友情链接更多精彩内容