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