Swing组件国际化

很长一段时间一直在做 Applet 开发,前不久客户提出要做界面的国际化。我也有一直思考这个问题, Java 本身对国际化支持很好的,最简单的方式就是在创建每个 Component 之前,把文本就国际化然后再塞给这个组件。例如: (resources 是根据 Locale 读入的 ResourceBundle)
java 代码
  1. JFrame frame = new JFrame();   
  2. frame.setTitle(resources.getString("Title"));   
这样做实在太不雅,另外也无法在运行时随着 Locale 改变而改变界面的语言。
在网上看到一篇文章 http://www.progdoc.de/papers/intSwing/intswing/intswing.html ,提到一种很有趣的做法,通过设置每个组件的 ComponentUI ,在 ComponentUI 渲染该组件之前,把文本国际化。
先介绍一下 ComponentUI Swing 组件是基于 MVC 模式的,但同经典的 MVC 有点不一样, Swing View Controller 合并到同一个委托对象中: ComponentUI ,该对象都有 paint() 方法负责渲染其关联的组件。
那么怎么知道每个组件用的什么 ComponentUI 来画这个组件呢?有一个叫 UIManager 的大总管来负责设置整个界面的样式,通过 setLookAndFeel(LookAndFeel) 方法设置 LookAndFeel ,而 LookAndFeel 的实现中即包括对所有组件的 ComponentUI 的设置。
例如: Windows 风格的界面
java 代码
  1. public class WindowsLookAndFeel extends BasicLookAndFeel{   
  2.              protected void initClassDefaults(UIDefaults table){   
  3.                     super.initClassDefaults(table);   
  4.     
  5.                     final String windowsPackageName = "com.sun.java.swing.plaf.windows.";   
  6.     
  7.                     Object[] uiDefaults = {   
  8.                        "ButtonUI", windowsPackageName + "WindowsButtonUI",   
  9.                          "CheckBoxUI", windowsPackageName + "WindowsCheckBoxUI",   
  10.                                  "CheckBoxMenuItemUI", windowsPackageName + "WindowsCheckBoxMenuItemUI",   
  11.                               "LabelUI", windowsPackageName + "WindowsLabelUI",   
  12.                             "RadioButtonUI", windowsPackageName + "WindowsRadioButtonUI",   
  13.                                     "RadioButtonMenuItemUI", windowsPackageName + "WindowsRadioButtonMenuItemUI",   
  14.                    ……  
设置渲染 Button 使用 WindowsButtonUI ,画 Label 使用 WindowsLabelUI….. 等等
所以我们要定制自己的国际化控件继承 WindowsLookAndFeel ,然后重载 initClassDefaults 方法,在其中插入自己指定的 ComponentUI
java 代码
  1. public class MLWindowsLookAndFeel extends WindowsLookAndFeel {   
  2.     protected void initClassDefaults(UIDefaults table) {   
  3.         super.initClassDefaults(table);   
  4.         Object[] classes = {   
  5.                            "LabelUI", mlPackage + "MLWindowsLabelUI",   
  6.                                                         ………………   
  7.         };   
  8.         table.putDefaults(classes);   
  9.     }   
  10. }  
这样任何一个 JLabel 显示的时候都调用我们自定义的 MLWindowsLabelUI 中的 paint() 方法
我们就可以在这个地方做手脚了
java 代码
  1. public class MLWindowsLabelUI extends WindowsLabelUI {   
  2.     private final static MLWindowsLabelUI ML_WINDOWSLLABEL_UI = new  
  3.             MLWindowsLabelUI();   
  4.     /**  
  5.      * 必须重载该方法,因为UIManager会调用该方法获得其实例,所以不重写会获得其父类的实例  
  6.      * @param c JComponent  
  7.      * @return ComponentUI  
  8.      */  
  9.     public static ComponentUI createUI(JComponent c) {   
  10.         //因为树上的Cell和下拉框中显示也用了Label控件,但其无需国际化,返回其父类的实例   
  11.         if(c instanceof TreeCellRenderer ||c instanceof ListCellRenderer){   
  12.             return new WindowsLabelUI();   
  13.         }   
  14.         return ML_WINDOWSLLABEL_UI;   
  15.     }   
  16.     /**  
  17.      * 在格式化Label的文本前,将其国际化,再调用父类的格式文本方法,  
  18.      * 若在这之后重载其他方法以实现国际化会不正确,因为已格式化的文本可能跟原来不一样  
  19.      * 比如过长的文体会用...代替  
  20.      */  
  21.     protected String layoutCL(JLabel label,FontMetrics fontMetrics,String text,Icon icon,Rectangle viewR,Rectangle iconR,Rectangle textR) {   
  22. return super.layoutCL(label,fontMetrics,MessageUtil.getMessage(text),icon,viewR,iconR, textR);   
  23.     }   
  24. }  
有两个地方需要注意的:首先必须重载父类的 createUI 方法,否则当调用该类的 createUI 是返回的还是父类的实现,所以不会有任何效果,因为这个我 Debug 了好长时间。
再就是,我们也并不会真的去重载 paint() 方法,因为要画一个控件是个很麻烦的事情,我们只需要找出其画需要的文本的那个方法然后重载,调用其父类对应方法传入已国际化的文本就 OK 了。当然要找出合适的方法重载也有点麻烦,需要大概把 paint() 执行流程通读一边。对于 Label, 我之前是重载 paintEnabledText() ,但发现当 Label 长度不够时,文本就会变成省略号,结果以这个为 Key 去查资源文件肯定不对,所以要找到合适的方法可能需要反复试验。
使用直接继承的方式,现在只能局限于 Windows 风格的界面,可考虑使用装饰模式,我们的 LookAndFeel 直接继承 BasicLookAndFeel ,然后构造方法中传入另一个 LookAndFeel 的实例,将其他方法的实现都委托给该 LookAndFeel LabelUI 也使用同样的方式。这样应该就可适用于其他风格的界面了。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值