您的位置: 文章阅读 ┆ 回首页
 
站内资源搜索:
┆ 将本文寄给朋友  

Swing框架之UI Delegate之一

 

作者:WilliamChen

 

 
     Swing的UI Delegate机制使得Swing组件可以动态地切换LAF。注:所谓LAF就是Look and feel,外观和感觉,为了更准确表达这个意思,后文用LAF,表示Look And Feel。这套LAF机制是封装在Swing组件内部的,开发者编写Swing程序,不需要指定特定LAF。Swing工具提供了一套缺省LAF;当然Swing的LAF的API是开放的,允许开发者通过扩展已有的LAF或者从头创建一个LAF。虽然这种可插拔LAF的API是可扩展的,但是它被有意设计成基本组件下一层的接口,这样开发者不必要理解LAF机制的复杂细节就能创建Swing图形用户界面。


                                                                                     
        虽然不提倡开发者自己创建新的LAF,但Swing开发小组意识到了PLAF(Platform Look And Feel, 平台相关的外观和感觉)对于想创建独特外观的应用程序来说是一个非常强大的功能。事实证明,PLAF对于创建残障用户友好的界面是很理想的(残障用户:有视力缺陷的用户或者不能操作鼠标的用户)。   

       本质上说,可插拔LAF的设计意味着实现组件展现(Look)和事件处理(Feel)的部分是被代理到独立的UI对象中的,这些UI对象是由当前的LAF提供的,它们可以被动态地修改。

        这些可插拔LAF的API包括:

  • Swing组件(component)类中的钩子
  • LAF管理的顶层API
  • 独立的包中实际实现LAF的接口

组件钩子

        拥有有LAF特征的Swing组件在javax.swing.plaf包都有一个抽象类来代表它的UI Delegate,这些UI类的命名规则是该组件类去掉前缀J,加上后缀UI。比如JButton的UI Delegate类的名字是ButtonUI。

        UI Delegate在组件的构造函数中创建,并以组件的限定JavaBean属性的形式访问,比如JScrollBar提供了下面的方法访问它的UI Delegate:

   public ScrollBarUI getUI()
   public void setUI(ScrollBarUI ui)

        组件创建并设置UI Delegate的过程实际上是“安装”组件LAF的过程。每个组件同时提供方法创建和设置“缺省”LAF的UI Delegate,组件的构造函数在安装UI时使用该方法。

   public void updateUI()

        LAF实现为每个抽象UI类提供了具体子类,比如,Windows LAF定义了WindowsButtonUI, WindowsScrollBar等。当组件安装它的UI Delegate时,须有办法动态查找到当前缺省LAF的具体实现类名。该操作使用了一个hash表,它的主键是由组件的getUIClassID()方法获得,习惯是使用平台抽象类名,比如JScrollBar的getUIClassID()是这样定义的:

public String getUIClassID() {
   return "ScrollBarUI"; 
}

        相应在此hash表中,Windows LAF将他ScrollBarUI映射成JScrollBar在Windows平台的UI实现类:

com.sun.java.swing.plaf.windows.WindowsScrollBarUI

LAF管理

        Swing定义了一个抽象类LookAndFeel来表达所有LAF实现的核心信息,比如LAF的名字、描述、是否是本地化LAF,以及一个Hash表(也称作“Defaults Table”)来存储各种各样LAF属性的缺省值,比如颜色和字体。每种LAF实现都定义一个LookAndFeel的子类,如swing.plaf.motif.MotifLookAndFeel,来为Swing提供管理LAF的必须信息。

        UIManager是组件和程序访问LAF信息的API(尽量不要直接访问LookAndFeel实例)。 UIManager负责跟踪当前有哪些LookAndFeel类可用,哪些安装了,谁是缺省的。UIManager还管理对于当前LAF的Defaults Table的访问。

“缺省”LAF

        UIManager还提供了设置和获取当前缺省LookAndFeel的方法:

   public static LookAndFeel getLookAndFeel()
   public static void setLookAndFeel(LookAndFeel newLookAndFeel)     
   public static void setLookAndFeel(String className)

        Swing初始化了一个跨平台Java外观(以前称作“Metal”)作为缺省的LAF。然而,当Swing程序想明确设置缺省LAF时,可使用UIManager.setLookAndFeel()方法,比如下面的代码将缺省的LAF设置成CDE/Motif:

UIManager.setLookAndFeel("com.sun.java.swing.plaf.motif.MotifLookAndFeel");

        有时应用程序并不想指定特定的LAF,而想动态匹配当前平台类型的LAF,比如运行在Windows NT上就使用Windows LAF,运行在Solaris上就使用CDE/Motif,或者程序想设置成为跨平台的Java LAF,UIManager为这些情况提供了下面的静态方法,动态地获取合适LookAndFeel的类名:

  public static String getSystemLookAndFeelClassName()
  public static String getCrossPlatformLookAndFeelClassName()

        为确保程序总是使用系统LAF,可使用下面的代码:

UIManager.setLookAndFeel(UIManager.getSystemLookAndFeelClassName());

动态调整缺省的LAF

        Swing程序使用上面方法动态地设置LAF时,理想位置是在任何Swing组件初始化以前,因为UIManager.setLookAndFeel()方法通过装载并初始化LookAndFeel实例来使它成为当前缺省的LAF,但是它不能自动使当前所有的组件改变它们的外观。记住组件在构造函数里初始化它们的UI Delegate对象,如果当前缺省LAF在它们构造函数完毕之后发生变化,它们不会相应地自动更新,需程序遍历组件树更新组件来实现外观切换,Swing在SwingUtilities中提供了updateComponentTreeUI()方法来帮程序员完成此过程。

        组件外观可在任何时候通过调用它的updateUI()方法来切换到当前缺省的LAF,该方法调用如下UIManager的静态方法来获得合适的UI Delegate 对象:

   public static ComponentUI getUI(JComponent c)

        比如JScrollBar的updateUI()实现代码如下:

public void updateUI() {
   setUI((ScrollBarUI)UIManager.getUI(this));
}

        如果程序在初始化后需要改变它的GUI树的外观,可使用下面的代码:

// GUI已经初始化了,假设myframe变量是最顶层的frame
try {
   UIManager.setLookAndFeel("com.sun.java.swing.plaf.motif.MotifLookAndFeel");
   myframe.setCursor(Cursor.getPredefinedCursor(Cursor.WAIT_CURSOR));
   SwingUtilities.updateComponentTreeUI(myframe);
   myframe.validate();
} catch (UnsupportedLookAndFeelException e) {
} finally {
   myframe.setCursor(Cursor.getPredefinedCursor(Cursor.DEFAULT_CURSOR));
}

管理LAF数据

        UIManager定义了一个静态类UIManager.LookAndFeelInfo来存储LAF的名称(如“Metal”)及LAF类名如 com.sun.java.swing.plaf.MetalLookAndFeel,它在内部使用这些名字来管理LookAndFeel对象,这些信息能通过UIManager下面的静态方法访问获得:

   public static LookAndFeelInfo[] getInstalledLookAndFeels()
   public static void setInstalledLookAndFeels(LookAndFeelInfo[] infos)
          throws SecurityException
   public static void installLookAndFeel(LookAndFeelInfo info)
   public static void installLookAndFeel(String name, String className)

        这些方法可在编程中决定哪个LAF实现可用,当创建允许用户动态切换皮肤的界面时很有用。

LAF包

        javax.swing.plaf包中的UI Delegate类(ButtonUI,ScrollBarUI等)定义了组件可以用来访问UI Delegate对象的接口,这些类在早期Swing实现中使用的是接口,后来替换成了抽象类。这些平台API接口是所有LAF实现的基类。

        每个LAF实现提供这些抽象类的具体类,LAF实现定义的这些类是分别放在javax.swing.plaf包的子包中,如javax.swing.plaf.motif、javax.swing.plaf.metal等。LAF包中包含下面的文件:

  • LookAndFeel的子类,比如MetalLookAndFeel
  • 所有LAF的UI delegate类,比如MetalButtonUI、MetalTreeUI等。
  • 所有LAF的工具类,比如MetalGraphicsUtils、MetalIconFactory等。
  • 其他和LAF相关的资源,比如图像文件等。

        在实现各种Swing LAF中,有许多共性的东西,这些共同的代码被重构成一个基本LAF实现,称作basic LAF,各平台包括motif、windows等的LAF又继承basic LAF,basic LAF包支持桌面级别的LAF,比如Windows和CDE/Motif。Basic LAF包只是实现可插拔LAF的一个例子,Swing的LAF框架足够灵活,足够容纳其他的实现。许多第三方的LAF是基于basic LAF,当然更多的是基于metal、motif或者windows这些更为具体的LAF实现的。



来源



以上文章版权由原作者所有。未经同意,不得将其任何一部分复制、转载、发布等未授权操作。


Java学习室 — 陈伟波个人主页
E-mail: zz3zcwb@sina.com
COPY RIGHT 2005