Swing实时刷新的实现

之前说到要想看看Swing。不知道为啥我第一个想到的问题是界面语言显示的国际化问题。管他呢,既然随心所遇学,想到了就实现来看看。

想要国际化,首先还是要有国际化资源文件。
 

 我们所要做的就是在切换语言的时候,从对应的资源文件中读取值信息,显示出来。

要实时响应语言的变化,可以采用JDK自带的观察者方式来实现。
 
  
这里封装了一个ObserverCenter,用于统一发送被观察实例的变化状态信息。
 
  
  
  1. /** 
  2.  * 观察者中心,用于统一通知发送变更信 
  3.  *  
  4.  * @author lihzh 
  5.  * @date 2012-3-17 下午9:54:15 
  6.  */ 
  7. public class ObserveCenter extends Observable { 
  8.      
  9.     private static ObserveCenter observeCenter; 
  10.     private ObserveCenter(){} 
  11.      
  12.     public static ObserveCenter getInstance() { 
  13.         if (observeCenter == null) { 
  14.             observeCenter = new ObserveCenter(); 
  15.         } 
  16.         return observeCenter; 
  17.     } 
  18.      
  19.     /** 
  20.      * 发送变更消息, 相当于{@link #notifyChange(null)} 
  21.      *  
  22.      * @author lihzh 
  23.      * @date 2012-3-17 下午9:58:19 
  24.      */ 
  25.     public void notifyChange() { 
  26.         notifyChange(null); 
  27.     } 
  28.  
  29.     /** 
  30.      * 发送变更信息 
  31.      *  
  32.      * @param obj 
  33.      * @author lihzh 
  34.      * @date 2012-3-17 下午10:01:50 
  35.      */ 
  36.     public void notifyChange(Object obj) { 
  37.         setChanged(); 
  38.         notifyObservers(obj); 
  39.     } 
  40.  
LocaleHandler类用于设置区域信息,同时在设置区域信息后,发送通过ObserverCenter发送状态改变通知。
 
  
  
  1. /** 
  2.  * 当前位置(国际化信息所用)管理类 
  3.  *  
  4.  * @author lihzh 
  5.  * @date 2012-3-17 下午4:26:43 
  6.  */ 
  7. public class LocaleHandler { 
  8.  
  9.     private static final Logger logger = LoggerFactory.getLogger(LocaleHandler.class); 
  10.      
  11.     public static final Locale DEFAULT_LOCAL = Locale.CHINA; 
  12.     private static Locale locale = DEFAULT_LOCAL; 
  13.  
  14.     static { 
  15.         ObserveCenter.getInstance().addObserver(new I18nUIHandler()); 
  16.         ObserveCenter.getInstance().addObserver(new ResourceBundleLoader()); 
  17.     } 
  18.  
  19.     /** 
  20.      * 获取国际化区域信息 
  21.      *  
  22.      * @return 
  23.      * @author lihzh 
  24.      * @date 2012-3-17 下午9:32:13 
  25.      */ 
  26.     public static Locale getLocale() { 
  27.         return locale; 
  28.     } 
  29.  
  30.     /** 
  31.      * 设置国际化区域信息 
  32.      *  
  33.      * @param locale 
  34.      * @author lihzh 
  35.      * @date 2012-3-17 下午9:31:47 
  36.      */ 
  37.     public static void setLocale(Locale locale) { 
  38.         LocaleHandler.locale = locale == null ? DEFAULT_LOCAL : locale; 
  39.         logger.info("Current locale is: [{}]", LocaleHandler.locale.toString()); 
  40.         refresh(); 
  41.     } 
  42.  
  43.     /** 
  44.      * 设置国际化区域信息,相当于{@link #setLocale(DEFAULT_LOCAL)} 
  45.      *  
  46.      * @author lihzh 
  47.      * @date 2012-3-17 下午9:35:00 
  48.      */ 
  49.     public static void setLocale() { 
  50.         setLocale(DEFAULT_LOCAL); 
  51.     } 
  52.  
  53.     /** 
  54.      * 刷新国际化信息 
  55.      *  
  56.      * @author lihzh 
  57.      * @date 2012-3-17 下午9:31:58 
  58.      */ 
  59.     private static void refresh() { 
  60.         ObserveCenter.getInstance().notifyChange(); 
  61.     } 
  62.  
ResourceBundleLoader 类,用于根据区域设置加载对应的国际化配置文件。该类实现了Observer接口。在Locale信息变化时,会收到通知,改变需要加载的国际化文件。
 
  
  
  1. /** 
  2.  * 国际化文件读取类,提供静态方法和静态引用 
  3.  *  
  4.  * @author lihzh 
  5.  * @date 2012-3-17 下午4:07:24 
  6.  */ 
  7. public class ResourceBundleLoader implements Observer { 
  8.  
  9.     private static final String RESOURCE_CLASSLOADER_PATH_PREFIX = "i18n.i18n_"
  10.     private static String RESOURCE_CLASSLOADER_PATH_FULL; 
  11.     private static ResourceBundle resourceBundle; 
  12.  
  13.     /** 
  14.      * 读取国际化文件中定义的值 
  15.      *  
  16.      * @param key 
  17.      * @return 
  18.      * @author lihzh 
  19.      * @date 2012-3-17 下午4:33:32 
  20.      */ 
  21.     public static String getValue(String key) { 
  22.         return resourceBundle.getString(key); 
  23.     } 
  24.  
  25.     /* 
  26.      * 当Locale环境变量改变时更改resourceBundle实例加载的国际化文件 
  27.      */ 
  28.     @Override 
  29.     public void update(Observable o, Object arg) { 
  30.         refreshBundle(); 
  31.     } 
  32.  
  33.     /** 
  34.      * 刷新读取的Bundle信息 
  35.      *  
  36.      * @author lihzh 
  37.      * @date 2012-3-17 下午10:13:26 
  38.      */ 
  39.     private static void refreshBundle() { 
  40.         RESOURCE_CLASSLOADER_PATH_FULL = RESOURCE_CLASSLOADER_PATH_PREFIX 
  41.                 + getLocale().toString(); 
  42.         resourceBundle = ResourceBundle.getBundle( 
  43.                 RESOURCE_CLASSLOADER_PATH_FULL, getLocale()); 
  44.     } 
至此,剩下的工作就是刷新当前系统中的所有控件了!
我想我们当然不希望每个空间都实现一个监听,各自进行刷新。这样虽然可以实现功能,但是开发效率是比较低下的,而且也不易维护。所以,我考虑用一个“控件池”ComponentPool去统一管理当前系统中的所有控件。
 
  
  
  1. /** 
  2.  * UI控件池,用于保存当前所有的UI控件 
  3.  *  
  4.  * @author lihzh 
  5.  * @date 2012-3-18 下午10:21:14 
  6.  */ 
  7. public class ComponentPool { 
  8.  
  9.     // 按钮池 
  10.     private static final Map<String, GenericButton> buttonPool = new HashMap<String, GenericButton>(); 
  11.  
  12.     /** 
  13.      * 注册按钮 
  14.      *  
  15.      * @param button 
  16.      * @author lihzh 
  17.      * @date 2012-3-18 下午10:26:35 
  18.      */ 
  19.     public static void addButton(GenericButton button) { 
  20.         if (button == null) { 
  21.             throw new IllegalArgumentException( 
  22.                     "The registing button can not be null."); 
  23.         } 
  24.         if (button.getName() == null) { 
  25.             throw new IllegalArgumentException( 
  26.                     "The registing button's name can not be null."); 
  27.         } 
  28.         buttonPool.put(button.getName(), button); 
  29.     } 
  30.  
  31.     /** 
  32.      * 获取按钮 
  33.      *  
  34.      * @param buttonName 
  35.      * @return 
  36.      * @author lihzh 
  37.      * @date 2012-3-18 下午10:30:31 
  38.      */ 
  39.     public static GenericButton getButton(String buttonName) { 
  40.         return buttonPool.get(buttonName); 
  41.     } 
  42.  
  43.     /** 
  44.      * 获取所有的Button 
  45.      *  
  46.      * @return 
  47.      * @author lihzh 
  48.      * @date 2012-3-18 下午10:52:50 
  49.      */ 
  50.     public static Map<String, GenericButton> getAllButtons() { 
  51.         return buttonPool; 
  52.     } 
  53.  
我们以Button为例。首先定义一个Button类型控件的基类GenericButton,所有具体的Button都需继承自该基类。
 
  
  
  1. /** 
  2.  * 所有按钮的基类,可统一处理国际化通用事件, 统一将name设置为国际化文件中的key值 
  3.  *  
  4.  * @author lihzh 
  5.  * @date 2012-3-18 下午5:15:01 
  6.  */ 
  7. public class GenericButton extends JButton { 
  8.  
  9.     private static final long serialVersionUID = -5971509297727392232L; 
  10.  
  11.     public GenericButton(String name) { 
  12.         super(); 
  13.         this.setName(name); 
  14.         ComponentPool.addButton(this); 
  15.     } 
  16.      
  17.     /* 
  18.      * 设置名字,同时设置显示值 
  19.      * @see java.awt.Component#setName(java.lang.String) 
  20.      * 
  21.      * @author lihzh 
  22.      * @date 2012-3-19 下午12:34:28 
  23.      */ 
  24.     @Override 
  25.     public void setName(String name) { 
  26.         super.setName(name); 
  27.         setText(ResourceBundleLoader.getValue(name)); 
  28.     } 
  29.      
  30.     /** 
  31.      * 在国际化位置发生改变时执行 
  32.      *  
  33.      * @author lihzh 
  34.      * @date 2012-3-18 下午10:35:54 
  35.      */ 
  36.     public void onLocaleChange() { 
  37.         this.setText(ResourceBundleLoader.getValue(this.getName())); 
  38.         afterTextChange(); 
  39.     } 
  40.  
  41.     /** 
  42.      * 在国际化位置发生改变时执行,用于子类复写其特有操作。在文本刷新前执行 
  43.      *  
  44.      * @author lihzh 
  45.      * @date 2012-3-18 下午10:57:06 
  46.      */ 
  47.     protected void beforeTextChange() {      
  48.     } 
  49.      
  50.     /** 
  51.      * 在国际化位置发生改变时执行,用于子类复写其特有操作。在文本刷新后执行 
  52.      *  
  53.      * @author lihzh 
  54.      * @date 2012-3-18 下午10:57:06 
  55.      */ 
  56.     protected void afterTextChange() {       
  57.     } 
该基类的构造函数中统一处理了Button显示的文字信息,同时将当前Button注册到了Pool中。
我们同样的用I18UIHandler类去实现了Observer接口,同时观察Locale变化的事件(注意与ResourceBundleLoader的先后顺序)。
 
  
  
  1. /** 
  2.  * UI界面国际化管理类 
  3.  *  
  4.  * @author lihzh 
  5.  * @date 2012-3-18 下午10:09:29 
  6.  */ 
  7. public class I18nUIHandler implements Observer { 
  8.  
  9.     /* 
  10.      * @see java.util.Observer#update(java.util.Observable, java.lang.Object) 
  11.      * 
  12.      * @author lihzh 
  13.      * @date 2012-3-19 下午12:33:27 
  14.      */ 
  15.     @Override 
  16.     public void update(Observable o, Object arg) { 
  17.         // 刷新按钮 
  18.         refreshButton(); 
  19.  
  20.     } 
  21.  
  22.     /** 
  23.      * 刷新按钮 
  24.      *  
  25.      * @author lihzh 
  26.      * @date 2012-3-18 下午10:53:02 
  27.      */ 
  28.     private void refreshButton() { 
  29.         Map<String, GenericButton> buttons = ComponentPool.getAllButtons(); 
  30.         Iterator<Entry<String, GenericButton>> butEntryIt = buttons.entrySet().iterator(); 
  31.         while (butEntryIt.hasNext()) { 
  32.             Entry<String, GenericButton> entry = butEntryIt.next(); 
  33.             GenericButton button = entry.getValue(); 
  34.             button.onLocaleChange(); 
  35.         } 
  36.     } 
  37.  
在接口到变化后,从池中分类获取所有控件。例如现在只有按钮空间,则从池中获取所有的按钮,然后循环调用基类中onLocaleChange()方法,即可实现控件文字的动态刷新。如图:

 系统中其他控件均可按此方式,实现国际化的动态切换。这样一个简单的Swing国际化框架的雏形就完成了。

具体控件代码举例:
 
  
  
  1. /** 
  2.  * 中文按钮 
  3.  *  
  4.  * @author lihzh 
  5.  * @date 2012-3-18 下午11:00:46 
  6.  */ 
  7. public class CNConfirmButton extends GenericButton { 
  8.  
  9.     private static final long serialVersionUID = 1481761153745275594L; 
  10.      
  11.     public CNConfirmButton(String name) { 
  12.         super(name); 
  13.         setBounds(100808030); 
  14.         addActionListener(new ActionListener() { 
  15.             @Override 
  16.             public void actionPerformed(ActionEvent e) { 
  17.                 LocaleHandler.setLocale(Locale.CHINA); 
  18.             } 
  19.         }); 
  20.     } 
  21.      
容器代码举例:
 
  
  
  1. /** 
  2.  * @author lihzh 
  3.  * @date 2012-3-15 下午10:57:16 
  4.  */ 
  5. public class MainContainer extends JFrame { 
  6.  
  7.     private static final long serialVersionUID = -4379994966214808467L; 
  8.     private static final Logger logger = LoggerFactory 
  9.             .getLogger(MainContainer.class); 
  10.  
  11.     public MainContainer() { 
  12.  
  13.         logger.info("Begin to initialize the UI container."); 
  14.         this.setSize(300200); 
  15.         this.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE); 
  16.         this.setResizable(false); 
  17.         this.setTitle(getValue(TITLE_MAIN)); 
  18.         this.getContentPane().setLayout(null); 
  19.         JButton usButton = new USConfirmButton(COMBO_LANGUAGE_US); 
  20.         JButton cnButton = new CNConfirmButton(COMBO_LANGUAGE_ZH); 
  21.         this.add(usButton, null); 
  22.         this.add(cnButton, null); 
  23.     } 
  24.  
注:
1、这里的容器并未实现国际化的动态刷新。
2、代码import部分均为加上,需自行引用。有些静态方法和常量未加类名因为采用的是静态引用。
3、Log采用的是slf4j+logback
PS:所有源码已上传至GitHub:
git://github.com/lihongzheshuai/ForFun.git

 

  • 0
    点赞
  • 5
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值