之前说到要想看看Swing。不知道为啥我第一个想到的问题是界面语言显示的国际化问题。管他呢,既然随心所遇学,想到了就实现来看看。
我们所要做的就是在切换语言的时候,从对应的资源文件中读取值信息,显示出来。
要实时响应语言的变化,可以采用JDK自带的观察者方式来实现。
这里封装了一个ObserverCenter,用于统一发送被观察实例的变化状态信息。 -
-
-
-
-
-
- public class ObserveCenter extends Observable {
-
- private static ObserveCenter observeCenter;
- private ObserveCenter(){}
-
- public static ObserveCenter getInstance() {
- if (observeCenter == null) {
- observeCenter = new ObserveCenter();
- }
- return observeCenter;
- }
-
-
-
-
-
-
-
- public void notifyChange() {
- notifyChange(null);
- }
-
-
-
-
-
-
-
-
- public void notifyChange(Object obj) {
- setChanged();
- notifyObservers(obj);
- }
-
- }
LocaleHandler类用于设置区域信息,同时在设置区域信息后,发送通过ObserverCenter发送状态改变通知。 -
-
-
-
-
-
- public class LocaleHandler {
-
- private static final Logger logger = LoggerFactory.getLogger(LocaleHandler.class);
-
- public static final Locale DEFAULT_LOCAL = Locale.CHINA;
- private static Locale locale = DEFAULT_LOCAL;
-
- static {
- ObserveCenter.getInstance().addObserver(new I18nUIHandler());
- ObserveCenter.getInstance().addObserver(new ResourceBundleLoader());
- }
-
-
-
-
-
-
-
-
- public static Locale getLocale() {
- return locale;
- }
-
-
-
-
-
-
-
-
- public static void setLocale(Locale locale) {
- LocaleHandler.locale = locale == null ? DEFAULT_LOCAL : locale;
- logger.info("Current locale is: [{}]", LocaleHandler.locale.toString());
- refresh();
- }
-
-
-
-
-
-
-
- public static void setLocale() {
- setLocale(DEFAULT_LOCAL);
- }
-
-
-
-
-
-
-
- private static void refresh() {
- ObserveCenter.getInstance().notifyChange();
- }
-
- }
ResourceBundleLoader 类,用于根据区域设置加载对应的国际化配置文件。该类实现了Observer接口。在Locale信息变化时,会收到通知,改变需要加载的国际化文件。 -
-
-
-
-
-
- public class ResourceBundleLoader implements Observer {
-
- private static final String RESOURCE_CLASSLOADER_PATH_PREFIX = "i18n.i18n_";
- private static String RESOURCE_CLASSLOADER_PATH_FULL;
- private static ResourceBundle resourceBundle;
-
-
-
-
-
-
-
-
-
- public static String getValue(String key) {
- return resourceBundle.getString(key);
- }
-
-
-
-
- @Override
- public void update(Observable o, Object arg) {
- refreshBundle();
- }
-
-
-
-
-
-
-
- private static void refreshBundle() {
- RESOURCE_CLASSLOADER_PATH_FULL = RESOURCE_CLASSLOADER_PATH_PREFIX
- + getLocale().toString();
- resourceBundle = ResourceBundle.getBundle(
- RESOURCE_CLASSLOADER_PATH_FULL, getLocale());
- }
- }
我想我们当然不希望每个空间都实现一个监听,各自进行刷新。这样虽然可以实现功能,但是开发效率是比较低下的,而且也不易维护。所以,我考虑用一个“控件池”ComponentPool去统一管理当前系统中的所有控件。 -
-
-
-
-
-
- public class ComponentPool {
-
-
- private static final Map<String, GenericButton> buttonPool = new HashMap<String, GenericButton>();
-
-
-
-
-
-
-
-
- public static void addButton(GenericButton button) {
- if (button == null) {
- throw new IllegalArgumentException(
- "The registing button can not be null.");
- }
- if (button.getName() == null) {
- throw new IllegalArgumentException(
- "The registing button's name can not be null.");
- }
- buttonPool.put(button.getName(), button);
- }
-
-
-
-
-
-
-
-
-
- public static GenericButton getButton(String buttonName) {
- return buttonPool.get(buttonName);
- }
-
-
-
-
-
-
-
-
- public static Map<String, GenericButton> getAllButtons() {
- return buttonPool;
- }
-
- }
我们以Button为例。首先定义一个Button类型控件的基类GenericButton,所有具体的Button都需继承自该基类。 -
-
-
-
-
-
- public class GenericButton extends JButton {
-
- private static final long serialVersionUID = -5971509297727392232L;
-
- public GenericButton(String name) {
- super();
- this.setName(name);
- ComponentPool.addButton(this);
- }
-
-
-
-
-
-
-
-
- @Override
- public void setName(String name) {
- super.setName(name);
- setText(ResourceBundleLoader.getValue(name));
- }
-
-
-
-
-
-
-
- public void onLocaleChange() {
- this.setText(ResourceBundleLoader.getValue(this.getName()));
- afterTextChange();
- }
-
-
-
-
-
-
-
- protected void beforeTextChange() {
- }
-
-
-
-
-
-
-
- protected void afterTextChange() {
- }
该基类的构造函数中统一处理了Button显示的文字信息,同时将当前Button注册到了Pool中。 我们同样的用I18UIHandler类去实现了Observer接口,同时观察Locale变化的事件(注意与ResourceBundleLoader的先后顺序)。 -
-
-
-
-
-
- public class I18nUIHandler implements Observer {
-
-
-
-
-
-
-
- @Override
- public void update(Observable o, Object arg) {
-
- refreshButton();
-
- }
-
-
-
-
-
-
-
- private void refreshButton() {
- Map<String, GenericButton> buttons = ComponentPool.getAllButtons();
- Iterator<Entry<String, GenericButton>> butEntryIt = buttons.entrySet().iterator();
- while (butEntryIt.hasNext()) {
- Entry<String, GenericButton> entry = butEntryIt.next();
- GenericButton button = entry.getValue();
- button.onLocaleChange();
- }
- }
-
- }
在接口到变化后,从池中分类获取所有控件。例如现在只有按钮空间,则从池中获取所有的按钮,然后循环调用基类中onLocaleChange()方法,即可实现控件文字的动态刷新。如图: 系统中其他控件均可按此方式,实现国际化的动态切换。这样一个简单的Swing国际化框架的雏形就完成了。
-
-
-
-
-
-
- public class CNConfirmButton extends GenericButton {
-
- private static final long serialVersionUID = 1481761153745275594L;
-
- public CNConfirmButton(String name) {
- super(name);
- setBounds(100, 80, 80, 30);
- addActionListener(new ActionListener() {
- @Override
- public void actionPerformed(ActionEvent e) {
- LocaleHandler.setLocale(Locale.CHINA);
- }
- });
- }
-
- }
-
-
-
-
- public class MainContainer extends JFrame {
-
- private static final long serialVersionUID = -4379994966214808467L;
- private static final Logger logger = LoggerFactory
- .getLogger(MainContainer.class);
-
- public MainContainer() {
-
- logger.info("Begin to initialize the UI container.");
- this.setSize(300, 200);
- this.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
- this.setResizable(false);
- this.setTitle(getValue(TITLE_MAIN));
- this.getContentPane().setLayout(null);
- JButton usButton = new USConfirmButton(COMBO_LANGUAGE_US);
- JButton cnButton = new CNConfirmButton(COMBO_LANGUAGE_ZH);
- this.add(usButton, null);
- this.add(cnButton, null);
- }
-
- }
2、代码import部分均为加上,需自行引用。有些静态方法和常量未加类名因为采用的是静态引用。 git://github.com/lihongzheshuai/ForFun.git
本文转自mushiqianmeng 51CTO博客,原文链接:http://blog.51cto.com/mushiqianmeng/810878,如需转载请自行联系原作者