kermart

k

 

分层Pane结构与Swing组件高级特性的实现

  这个题目起的有点难懂,但也实在想不出更好的题目来。所谓分层Pane结构是指JFrame/JApplet/JDialog等Swing顶层容器的JRootPane/JLayeredPane/GlassPane/ContentPane结构。所谓Swing组件高级特性其实是指某些组件的特殊功能的实现,比如弹出菜单、Tooltip、JComboBox的下拉窗口、Drag and Drop实现、Docking Pallete窗口等等。这些特性同普通组件不同,它们往往需要动态的变化、覆盖其他组件,它们之间存在一定层次关系。那么Swing中是怎么样实现这些功能呢?
  一直以来想写一篇文章来描述这个关键结构,但每次总被自己的语言表达能力所限制。Swing这个精巧结构是我叹服的原因之一。虽然一般的GUI系统设计原理我都知道,但是细节如此处精细却不是一开始就能清楚的。我在2000年以前曾经写过大量基于AWT Canvas的自定义组件,当时认为Swing的自定义组件也不过如此,原理不过也是给我一个刷子,我给你画出来。但是上述所说的这些特性,却从来没能实现过。我想过很多方法,做过很多实验,但终究没有想透这一层。之后某次偶然机会,看到了下面这张著名的图,使我豁然开朗。让我又一次理解了创新需要跳出旧的思维模式勇气和智慧。

  简单来说Swing中这些顶层容器的多Pane结构是实现这些特性的基础。这些Pane实际是一些特殊的JComponent,它们之间存在包含被包含、覆盖被覆盖的树状多层次结构。我重新画了上面这张图,使得其更具体、更直观,更容易理解些:

其实有了这张图,加上图形系统中Z-order的概念,就不难理解并实现上面提到的特性。Z-order概念和图形学中Z深度是一致的。离观察点近的物体总是会遮挡离观察点远的物体。物体所在法平面离观察点的距离就是所谓Z深度。Swing中通过先画出Z-order远(小)的组件,再画Z-order近(大)的组件的方法实现组件之间的遮挡关系。每个组件所在平面的都有一个数字描述其位置,这个位置同三维坐标系中的Z轴类似,离观察者越近,坐标越大:

  Swing的顶层容器都包含有一个JRootPane,该JRootPane是一切Swing组件起点。JRootPane中包含了一个JLayeredPane和一个GlassPane。GlassPane和JLayeredPane都是充满JRootPane的。GlassPane缺省情况下是不可见的,因此我们看到都是JLayeredPane。GlassPane如果是可见的,它Z-order大于任何其他组件,因此它会覆盖住整个窗口,使得所有的鼠标事件都被它截获。另外通常可见情况下它是透明的,因此你能仍然看到JLayeredPane上面的一切,但是JLayeredPane上面的组件都得不到鼠标事件。
GlassPane这个奇怪的组件主要是用来实现Drag & Drop以及跨组件渲染用的。NetBeans和Eclipse中哪种常见的Docking Frame的实现就和这种组件相关。这些工程、文件、源代码的窗口其实不过是普通的Swing组件,它们本身并不能实现这种拖拽功能。鼠标在它们上面标题栏区域按下之后,标题栏组件会检测到这种事件,经过粘连性判断后,如果发现这些鼠标事件目的是拖拽窗口,这种Docking系统就会将拖拽区域(即所谓的ClientArea)的GlassPane设置为可见,于是下面的一些列拖拽鼠标事件就被这个GlassPane所接管了过去。GlassPane是覆盖于应用程序ClientArea的透明组件,它处理这些事件时计算出当前鼠标位置所蕴含着的拖拽动作,并根据这些动作画出相应的焦点矩形。NetBeans的Docking Framework一个拖拽过程如下图所示.注意GlassPane是背景透明的,所以可以在上面画背景透明的焦点矩形:

  JLayeredPane是实现弹出式窗口或类似Pallete浮动窗口的主要组件。如同它的名字一样,它将自己的内部结构也分成许多亚层。在使用它的add(Component, Object)方法加入组件时,第二个参数是一个Integer值,这个值决定了加入的层。这个值相当于前面所说的Z-order值。目前主要有下面几个预定义值:
public final static Integer DEFAULT_LAYER = new Integer(0);
这层加入的缺省层。
  public final static Integer PALETTE_LAYER = new Integer(100);
这层是定义Palette窗口的层。那种浮动选项窗口属于这一层。
  public final static Integer MODAL_LAYER = new Integer(200);
这层是模态对话框的层。这个模态对话框应该是指JInternalFrame的模态对话框,而不是JDialog。
  public final static Integer POPUP_LAYER = new Integer(300);
这层是菜单、下拉框窗口、Tooltip等窗口浮动的层。
  public final static Integer DRAG_LAYER = new Integer(400);
这一层是拖拽层,组件可以在这一层被托拽。
  public final static Integer FRAME_CONTENT_LAYER = new Integer(-30000);
这一层是ContentPane和MenuBar所在的层。注意它非常小,前面所有层的组件都会覆盖这一层的组件。我们知道ContentPane是所有应用程序组件所在的地方。
  JLayeredPane直接包含有ContentPane组件。应用程序如果定了MenuBar,JLayeredPane还包含MenuBar。注意JLayeredPane本身没有布局管理器,它对组件的布局是由它的父容器JRootPane的布局管理器RootLayout来完成的。简单来说,所在层数值小的组件有可能被高层组件所覆盖。Swing将不同类型的组件放置在不同层面上,就实现了文章一开始提到的特性:菜单、浮动窗口、下拉框窗口和Tooltip等。当然这些窗口有可能不是JLayeredPane上的轻量级Swing组件,当它们的边界超过顶层容器的窗口时,这些窗口的就变成了重量级AWT窗口。这在上一篇文章《如何混排Swing和AWT组件中已经提到过。
  下面是这些Pane组件之间的树状包含关系图:

  本文的目的是讲述Swing的这种组件层次结构,并不是讲述如何使用JLayeredPane和GlassPane来实现某中特殊的功能。如果需要学习如何使用它们实现某些特殊效果,Java Tutorial的Swing部分提供了详尽的编程资料。Java Tutorial的Swing部分编程在:
  如何使用RootPane、GlassPane和JLayeredPane分别见下面的章节:

posted on 2012-07-26 11:21 kermart 阅读(1223) 评论(0)  编辑  收藏


只有注册用户登录后才能发表评论。


网站导航:
 

导航

统计

常用链接

留言簿

随笔档案

收藏夹

搜索

最新评论

阅读排行榜

评论排行榜