2007-08-10
Swing组件国际化
很长一段时间一直在做Applet开发,前不久客户提出要做界面的国际化。我也有一直思考这个问题,Java本身对国际化支持很好的,最简单的方式就是在创建每个Component之前,把文本就国际化然后再塞给这个组件。例如:(resources是根据Locale读入的ResourceBundle)
java 代码
- JFrame frame = new JFrame();
- 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 代码
- public class WindowsLookAndFeel extends BasicLookAndFeel{
- protected void initClassDefaults(UIDefaults table){
- super.initClassDefaults(table);
- final String windowsPackageName = "com.sun.java.swing.plaf.windows.";
- Object[] uiDefaults = {
- "ButtonUI", windowsPackageName + "WindowsButtonUI",
- "CheckBoxUI", windowsPackageName + "WindowsCheckBoxUI",
- "CheckBoxMenuItemUI", windowsPackageName + "WindowsCheckBoxMenuItemUI",
- "LabelUI", windowsPackageName + "WindowsLabelUI",
- "RadioButtonUI", windowsPackageName + "WindowsRadioButtonUI",
- "RadioButtonMenuItemUI", windowsPackageName + "WindowsRadioButtonMenuItemUI",
- ……
设置渲染Button使用WindowsButtonUI,画Label使用WindowsLabelUI…..等等
所以我们要定制自己的国际化控件继承WindowsLookAndFeel,然后重载initClassDefaults方法,在其中插入自己指定的ComponentUI:
java 代码
- public class MLWindowsLookAndFeel extends WindowsLookAndFeel {
- protected void initClassDefaults(UIDefaults table) {
- super.initClassDefaults(table);
- Object[] classes = {
- "LabelUI", mlPackage + "MLWindowsLabelUI",
- ………………
- };
- table.putDefaults(classes);
- }
- }
这样任何一个JLabel显示的时候都调用我们自定义的MLWindowsLabelUI中的paint()方法
我们就可以在这个地方做手脚了
java 代码
- public class MLWindowsLabelUI extends WindowsLabelUI {
- private final static MLWindowsLabelUI ML_WINDOWSLLABEL_UI = new
- MLWindowsLabelUI();
- /**
- * 必须重载该方法,因为UIManager会调用该方法获得其实例,所以不重写会获得其父类的实例
- * @param c JComponent
- * @return ComponentUI
- */
- public static ComponentUI createUI(JComponent c) {
- //因为树上的Cell和下拉框中显示也用了Label控件,但其无需国际化,返回其父类的实例
- if(c instanceof TreeCellRenderer ||c instanceof ListCellRenderer){
- return new WindowsLabelUI();
- }
- return ML_WINDOWSLLABEL_UI;
- }
- /**
- * 在格式化Label的文本前,将其国际化,再调用父类的格式文本方法,
- * 若在这之后重载其他方法以实现国际化会不正确,因为已格式化的文本可能跟原来不一样
- * 比如过长的文体会用...代替
- */
- protected String layoutCL(JLabel label,FontMetrics fontMetrics,String text,Icon icon,Rectangle viewR,Rectangle iconR,Rectangle textR) {
- return super.layoutCL(label,fontMetrics,MessageUtil.getMessage(text),icon,viewR,iconR, textR);
- }
- }
有两个地方需要注意的:首先必须重载父类的createUI方法,否则当调用该类的createUI是返回的还是父类的实现,所以不会有任何效果,因为这个我Debug了好长时间。
再就是,我们也并不会真的去重载paint()方法,因为要画一个控件是个很麻烦的事情,我们只需要找出其画需要的文本的那个方法然后重载,调用其父类对应方法传入已国际化的文本就OK了。当然要找出合适的方法重载也有点麻烦,需要大概把paint()执行流程通读一边。对于Label,我之前是重载paintEnabledText(),但发现当Label长度不够时,文本就会变成省略号,结果以这个为Key去查资源文件肯定不对,所以要找到合适的方法可能需要反复试验。
使用直接继承的方式,现在只能局限于Windows风格的界面,可考虑使用装饰模式,我们的LookAndFeel直接继承BasicLookAndFeel,然后构造方法中传入另一个LookAndFeel的实例,将其他方法的实现都委托给该LookAndFeel;LabelUI也使用同样的方式。这样应该就可适用于其他风格的界面了。
发表评论
提醒: 该博客已发表在公共论坛,博客所有留言会成为论坛回贴,留言请注意遵守论坛发贴规则
- 浏览: 40686 次
- 性别:

- 来自: 武汉

- 详细资料
搜索本博客
我的相册
vim-ide
共 4 张
共 4 张
最近加入圈子
最新评论
-
大学时候收到信
有一个真理:世上没有真理
-- by yanshiyi -
Linux C中也有“ThreadLo ...
int pthread_setspecific(pthread_key_t ke ...
-- by 七猫 -
Linux C中也有“ThreadLo ...
这里面主要是fs这个后来引入的寄存器造成的。linux有关nptl的设计文档里提 ...
-- by 七猫 -
Linux C中也有“ThreadLo ...
这个东东有一定的历史,说起来话长,高效TLS的设计在以前硬件不支持的时候比较麻烦 ...
-- by 七猫 -
Linux C中也有“ThreadLo ...
刚刚看到boost::asio里有一段用posix API 来实现thread ...
-- by fredzhang






评论排行榜