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
去统一管理当前系统中的所有控件。
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的先后顺序)。
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
);
1、这里的容器并未实现国际化的动态刷新。
2、代码import部分均为加上,需自行引用。有些静态方法和常量未加类名因为采用的是静态引用。
3、Log采用的是slf4j+logback
PS:所有源码已上传至GitHub:
git://github.com/lihongzheshuai/ForFun.git
Java实现图书管理系统
本篇文章是对目前Java专栏已有内容的一个总结练习,希望各位小主们在学习完面向对象的知识后,可以阅览本篇文章后,自己也动手实现一个这样的demo来加深总结应用已经学到知识并进行巩固。
Java实现拼图小游戏(1)—— JFrame的认识及界面搭建
如果要在某一个界面里面添加功能的话,都在一个类中,会显得代码难以阅读,而且修改起来也会很困难,所以我们将游戏主界面、登录界面、以及注册界面都单独编成一个类,每一个类都继承JFrame父类,并且在类中创建方法来来实现页面
Java实现拼图小游戏(7)—— 计步功能及菜单业务的实现
注意由于我们计步功能的步数要在重写方法中用到,所以不能将初始化语句写在方法体内,而是要写在成员位置。在其名字的时候也要做到“见名知意”,所以我们给它起名字为step
Java实现拼图小游戏(7)—— 作弊码和判断胜利
当我们好不容易把拼图复原了,但是一点提示也没有,完全看不出来是成功了,那么我们就需要有判断胜利的功能去弹出“成功”类的图片,以便于玩家选择是重新开始还是退出小游戏
Java实现拼图小游戏(6)—— 移动图片(键盘监听实操练习)
当我们实现向上移动图片的时候,其实就是把空图片的下面一张图片往上移动,然后将空图片的下面那张图片设置为空图片,最后再调整初始位置为现在空图片所在位置即可,注意做完这些以后还要再加载图片,否则显示不出来
Java实现拼图小游戏(5)—— 美化界面(含源码阅读)
先加载的图片在上方,后加载的图片在下方,所以我们要把创建背景图的代码放在我们之前创建十五个小图片的代码后面,否则会出现背景图片将我们要拼的图片覆盖的情况