Swing组件国际化 (转)很长一段时间一直在做Applet开发,前不久客户提出要做界面的国际化。我也有一直思考这个问题,Java本身对国际化支持很好的,最简单的方式就是在创建每个Component之前,把文本就国际化然后再塞给这个组件。例如:(resources是根据Locale读入的ResourceBundle) java 代码 - JFrame frame = new JFrame();
- frame.setTitle(resources.getString("Title"));
这样做实在太不雅,另外也无法在运行时随着Locale改变而改变界面的语言。 先介绍一下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();
-
-
-
-
-
- public static ComponentUI createUI(JComponent c) {
-
- if(c instanceof TreeCellRenderer ||c instanceof ListCellRenderer){
- return new WindowsLabelUI();
- }
- return ML_WINDOWSLLABEL_UI;
- }
-
-
-
-
-
- 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也使用同样的方式。这样应该就可适用于其他风格的界面了。 |