轻量级Swing组件概述

开发 后端
本文介绍轻量级Swing组件,Swing的GUI总是由顶层容器组件和轻量级swing组件组合建立,顶层容器和其他组件区别主要在于顶层容器没有自身的paint逻辑。

在Swing顶层重量级容器组件的一个绘制场景中,可以看到是经由awt-windows eventloop到了底层事件后触发paint绘制;然而对轻量级Swing组件,其paint都是通过java代码中对repaint调用而触发,其会向RepaintManager.addDirtyRegion,同时 scheduleProcessingRunnable。这是整个GUI生命周期内对绘制的两种不同的触发方式,但触发后的处理都是交由 RepaintManager。

回过头去看,JFrame被构造的时候就会创建root pane, layered pane,content pane, glass pane等,这些没有对等体的轻量级Swing组件在构造时都将repaint。虽然在创建windows对等窗口之前这些Swing组件就已经在要求绘制,但是RepaintManager能够协调好这个步调(具体即是当收到repaint请求时要判断情况,像这时的请求因为顶层容器还没有绘制则不会记录到重画区)。所以最终效果就是在peer.pshow的时候只能看到一个空窗口,随后底层消息到来后通过paint回调画这些子组件,***hello world才显示出来。如果眼神好,能够看出这有一个“闪烁”。
这是一个最简单的Swing应用程序的基本运行机制分析,下面再具体分析。

Swing的GUI总是由顶层容器组件和轻量级Swing组件组合建立,顶层容器和其他组件区别主要在于顶层容器没有自身的paint逻辑。所有顶层容器都是通过使用底层系统API来绘制对等体的方式进行paint,自身没有java2d的paint逻辑实现,对等体画成什么样顶层容器就是什么样,它只是可以控制对等体的一些可配显示属性。所以效果就是比如在windows平台上画一个jframe,除在桌面上显示一个窗口还会在任务栏上显示一个条目。Swing的 4个顶层容器都是在addNotify时才会getToolkit().createPeer(this)(Frame/Dialog/Window), 而addNotify并不是在构造时被调用,而是在pack/show或setvisible(这3个所谓的realized具现化方法)时被调用。创建了对等体peer后还要通过peer.pShow(show/setVisible(true)调用)调用才会要求底层系统进行显示(所以只有pack是不会显示窗口的)。在显示窗口后底层消息队列得到通知,此后随着窗口被最小化后恢复或被遮盖后恢复等系统操作后同样能从底层消息得到通知,这时的监听处理将有选择地通知给RepaintManager一个重画请求进行窗口内容-子组件重画。

而轻量级Swing组件将绘制有关的职责都委托给了ui成员对象,ui对象使用JAVA2D API 进行绘制,paint成什么样那就是这个组件的样子。具体就是在构造的时候即要 updateUI{setUI(UIManger.getUI(this))}。UIManger会根据当前L&F的选择,根据 this.uiClassID来得到ui成员类并建立实例,以后的paint回调等都推托给ui成员类paint,这也算是一种策略模式。Setui的过程中除了保存这个ui实例外,将repaint来通知RepaintManager进行paint回调完成组件绘制。轻量级Swing组件在addNotify时也会去创建对等体getToolkit().createPeer(this)( LightWeightPeer),但这个peer的实现(NullComponentPeer)是个空壳子,只是作为一个轻量级组件的标记,以后的很多事件处理等都要判断peer是否instance of LightWeightPeer从而能够进行不同处理。同样的Addnotify也不是在构造时被调用,而是在被加入container时被调用。

注意:构造方法本身就是状态模式的***状态,所以GUI组件的构造方法里就应该要努力完成自身的绘制来符合自己的地位。轻量级组件就是按这个意义在构造方法里去通知repaintmanager进行自身绘制的,但是顶层容器却将真正的绘制意图createPeer延迟到了具现方法里。这是因为首先一个合乎思维的表达逻辑是先有容器,再将子组件向容器里添加,所以最顶层容器总是先行构造出来,然后再被一层层地追加轻量级子组件。如果最顶层容器在构造时就去具现,则就要求后续的构造都应该在EDT中进行,而且每次add子组件都要导致revalidate;但若将最顶层容器的绘制分离延迟到具现方法里,则可以表达是在容器里盛满了要显示的子组件后再一股脑具现绘制出来的概念,类似于在进行一次web页面的完整加载,然后注意在具现方法执行后如果要操作组件都在EDT中进行即可,而且顶层容器提供一个特有的 pack方法,用来一次性对所有子组件验证大小位置进行重布局,pack之后再show,这样的一次性计算展现是最有效率的。

顶层容器和轻量级组件就是这样诞生并绘制的,在此后的生命周期里,都将按事件监听机制完成GUI随需而变,无论是系统事件,还是因为repaint调用主动post事件,事件到来后再在EDT中执行监听器里的paint绘制。Swing已经提供的顶层容器和轻量级组件因各自的定义已经注册了各自的paint监听,开发人员可以再行维护或按此模式开发新组件从而满足应用的需要。比如,jbutton默认有mousepress listener,在mousepress事件到来后,监听响应中会设置鼠标颜色加深来表示按下,然后再调用repaint要求重画,随后在EDT中执行 jbutton的paint回调,此时按深颜色绘制,于是一个被按下的效果就出来了。

下面在具体分析各类事件的处理。

对于顶层容器的受底层事件消息的触发,当得到的通知是因为expose暴露隐藏区(暴露被遮蔽的部分或恢复最小化或***次绘制等)时,处理过程会涉及到双缓存的处理,即如果可能,直接使用缓存中的旧图像信息进行覆盖而不再重新绘制。

所谓双缓存机制是将一整片的显示内容暂时写入一张内存空间里,然后一次性内存拷入显示区来进行显示,这样处理是因为如果直接写入显示区,随着显示区被该写入线程逐渐写入,可能经历多次屏幕刷新,导致每次刷新都形成过程图像,给人眼造成闪烁感觉;同时一个副收益就是可以针对每个窗口都做缓存待用(而不仅仅是针对一个屏幕双缓存),当窗口被遮挡的部分重现时直接拷贝缓存来覆盖,不用再执行绘画逻辑,提高了效率。

现在的OS一般都提供双缓存机制支持,如果底层系统自身支持以每个窗口为单位做双缓存,则该expose消息将被本地处理,不需要通知进行子组件的绘制;如果底层不支持,则该消息会到达wcomponetpeer.handleexpose中进行回调处理,此时Swing机制下有一个参数控制的双缓存机制可以提供。这里的参数控制需要从RepaintManager的构造过程说起。

首先RepaintManager可以通过static setCurrentManager(SomeCurrentManager)来进行全局指定。默认情况使用 currentRepaintManager(){new RepaintManager(BUFFER_STRATEGY_TYPE)}得到一个延迟创建的单例。RepaintManager有一段静态类初始化过程,涉及到双缓存设置:

  1. static{  
  2. nativeDoubleBuffering="true".equals(AccessController.doPrivileged(  
  3. newGetPropertyAction("awt.nativeDoubleBuffering")));//JVM的启动参数控制,默认false  
  4. Stringbs=AccessController.doPrivileged(  
  5. newGetPropertyAction("swing.bufferPerWindow"));//是否每窗口缓存。  
  6. if(headless){  
  7. BUFFER_STRATEGY_TYPE=BUFFER_STRATEGY_SPECIFIED_OFF;  
  8. }  
  9. elseif(bs==null){  
  10. BUFFER_STRATEGY_TYPE=BUFFER_STRATEGY_NOT_SPECIFIED;  
  11. }  
  12. elseif("true".equals(bs)){  
  13. BUFFER_STRATEGY_TYPE=BUFFER_STRATEGY_SPECIFIED_ON;  
  14. }  
  15. else{  
  16. BUFFER_STRATEGY_TYPE=BUFFER_STRATEGY_SPECIFIED_OFF;  
  17. }  
  18. }  
  19. privateRepaintManager(shortbufferStrategyType){  
  20. //Ifnativedoublebufferingisbeingused,doNOTuse  
  21. //Swingdoublebuffering.  
  22. doubleBufferingEnabled=!nativeDoubleBuffering;  
  23.  
  24. this.bufferStrategyType=bufferStrategyType;  
  25.  
  26. }  
  27. publicvoidsetDoubleBufferingEnabled(booleanaFlag){  
  28. doubleBufferingEnabled=aFlag;  
  29.  
  30. privatesynchronizedPaintManagergetPaintManager(){  
  31. if(paintManager==null){  
  32. PaintManagerpaintManager=null;  
  33. if(doubleBufferingEnabled&&!nativeDoubleBuffering){  
  34. switch(bufferStrategyType){  
  35. caseBUFFER_STRATEGY_NOT_SPECIFIED:  
  36. if(((SunToolkit)Toolkit.getDefaultToolkit()).  
  37. useBufferPerWindow()){//windows下是否禁用vistadwm,
    在没有声明bufferPerWindow的情况下由windows系统特性确定paintmanager。  
  38. paintManager=newBufferStrategyPaintManager();  
  39. }  
  40. break;  
  41. caseBUFFER_STRATEGY_SPECIFIED_ON:  
  42. paintManager=newBufferStrategyPaintManager();  
  43. break;  
  44. default:  
  45. break;  
  46. }  
  47. }  
  48. //nullcasehandledinsetPaintManager  
  49. setPaintManager(paintManager);  
  50. }  
  51. returnpaintManager;  
  52. }  
  53. voidsetPaintManager(PaintManagerpaintManager){  
  54. if(paintManager==null){  
  55. paintManager=newPaintManager();  
  56. }  
  57.  

【编辑推荐】

  1. 浅谈Swing中的Look&Feel
  2. Swing组件大全随着Java风潮一起来席卷国内
  3. 全面比较Swing与SWT
  4. Swing程序中调用JavaFX代码
  5. SWT和Swing代码的区别
责任编辑:佚名 来源: csdn
相关推荐

2009-07-17 14:38:51

轻量级Swing组件

2009-07-17 12:54:13

2009-07-16 11:19:48

外部线程Swing组件

2023-02-03 15:21:52

2023-09-27 00:12:23

2009-07-16 17:09:02

Swing应用程序

2009-09-11 08:26:49

Linux系统CRUX 2.6Linux

2023-08-09 08:01:38

场景Redis接口

2009-07-10 17:43:10

Swing.table

2009-07-16 16:46:39

Swing窗体

2016-10-14 16:35:39

2010-09-09 13:12:29

XML DOM

2009-06-23 08:56:55

Linux桌面搜索

2023-12-22 14:07:00

Go轻量级Goroutines

2022-08-10 12:21:07

PythonWebBottle

2020-11-11 12:13:59

JS

2022-07-15 16:39:19

PythonWhoosh工具

2009-07-10 18:06:59

JTree Swing

2009-07-17 16:32:34

Timer对象Swing

2009-07-14 09:09:08

Swing模型过滤器
点赞
收藏

51CTO技术栈公众号