研究Swing和AWT中的绘制首推《painting in awt and swing》,网上有翻译的中文版,还不错。不过如果基础不好,不是一遍就可以看懂的,要反复研读。
读完这篇文章后,我觉得我们要理解几个区别。
一、重量级组件与轻量级组件的区别。
一个错误的认识是AWT是老一代的GUI,它们都是重量级组件,而Swing是新的GUI,它们都是轻量级组件。其实不然,最明显的错误就是Swing中有几个重量级组件,如JFrame,JDialog等,另外,AWT也不是完全是重量级的,这可能不如上一点被人所接受。虽然很多人认是完全是,但事实上并不完全是。
重量级组件和轻量级组件有很多的不同,这种不同也为各自的应用带来了很多差异,比如轻量级组件可以使用双缓冲。我们在这里关注他们本质的不同所引发的绘制上的差异。
不同于重量级组件,轻量级组件来自于纯的java code。它的显示是借助于它的父容器,比如一个JFrame上的JPanel是利用JFrame来进行自己的绘制,这是最大的不同。学习重量级组件的显示,你需要关注对等实体,学习轻量级组件你需要关注repaintmanager。
重量级组件依靠对等实体来实现绘制,即peer,它利用本地操作系统来进行GUI的绘制。对等实体会在任务栏绘制一个条目。Swing的绘制是建立在重量级组件的绘制之上的,所以,新建一个Swing应用,你必须有一个JFrame,JDialog,Canvas等重量级组件作为载体。Swing通过一级一级的委托来实现绘制。而这当中的组织协调工作,你调用repaint之后,如何来安排绘制,都交给了RepaintManager来完成。由于这种机制,我们可以实现半透明的效果,因为我们可以让背景的像素保留一部分,而上层的也只显示一部分。
第二个需要注意的是paint与repaint方法。
可以将触发操作的原因分为两种,系统触发的绘画和程序触发的绘画。
在AWT层面上,这两种触发所引发的绘制过程稍有不同。对于系统引发的绘画,一般是直接将paint方法放入事件派发线程即可,而对于
程序引发的绘画,程序会调用部分的repaint方法,该方法会向AWT登记一个异步请求,AWT促使事件派发线程去调用部分的update方法。如果部件没有覆盖(override)update()方法,update()的默认实现会清除部件背景(如果部件不是“轻量级”),然后只是简单地调用paint()方法。 其实两者处理的流程不一样,但结果大致一样,都是paint方法被调用。但有了repaint和update,程序引发的绘制可以实现多次请求的合并,从而实现效率优化。通常情况下,我们需要覆盖paint方法来实现自定义的绘制,但我们不要去调用paint方法,因为他可能导致绘画过程的紊乱。我们需要调用的是repaint方法,它会发送一个异步的绘制请求。
对于轻量级组件来说,依然是通过覆盖paint,调用repaint来实现绘制,但由于轻量级组件存在着依赖关系,即组件依赖于父容器,在paint方法中,我们需要调用super.paint();因为我们可以定制组件自己的显示,但组件的子组件是由Swing来定义显示的,如果我们不调用
super方法,那子组件将不能被显示出来。
另外,我们在调用repaint()方法时,尽可能地调用有参数的repaint方法,因为这不会导致整个页面的重绘。
第三个需要注意的是Swing与轻量级。
很多人谈到Swing的绘制特性时,就将Swing的特性与轻量级的特性混为一谈。Swing的突出特点是对双缓冲的支持,是对UI代理,边框绘制,遮光性等的支持。