我最近遇到了这个问题,所以我将分享我尝试过的方法以及对我有用的方法。请注意,我还需要在运行时实现更改字体操作。
在 Swing 应用程序中,我们通常为核心容器扩展容器类。假设我们要为桌面创建 Facebook。有人可以创建 3 个核心类(扩展 JPanel 或 JScrollPane)。假设:LeftPanel extends JPanel、MiddlePanel extends JPanel 和 RightPanel extends JPanel。左侧面板代表左侧菜单,中间面板代表主滚动视图,最后右侧面板代表广告区域。当然,这些面板中的每一个都将继承JPanels(可能其中一些也会有一个新类,例如PostPanel、CommentSectionPanel 等)
现在,假设您已阅读 The Use of Multiple JFrames: Good or Bad Practice?,您的应用程序仅使用一个 JFrame,并且它托管其中的每个组件。甚至模态的JDialogs 也基于它(将其作为其父级)。因此,您可以将其保存在 Singleton 之类的地方。
为了更改组件文本,我们必须为每个组件调用setText。调用JFrame 的getComponents 将为我们提供它的所有组件。如果其中之一是容器,我们将不得不为它调用getComponents,因为它可能也包含组件。解决方案是递归查找所有这些:
private static <T extends Component> List<T> getChildren(Class<T> clazz, final Container container) {
Component[] components;
if (container instanceof JMenu)
components = ((JMenu) container).getMenuComponents();
else
components = container.getComponents();
List<T> compList = new ArrayList<T>();
for (Component comp : components) {
if (clazz.isAssignableFrom(comp.getClass())) {
compList.add(clazz.cast(comp));
}
if (comp instanceof Container)
compList.addAll(getChildren(clazz, (Container) comp));
}
return compList;
}
使用参数java.awt.Component.class 和myJFrame 调用此方法将为您提供JFrame 拥有的所有组件。
现在,我们必须区分哪些可以“刷新”(更改语言),哪些不能。让我们为此创建一个Interface:
public static interface LocaleChangeable {
void localeChanged(Locale newLocale);
}
现在,我们不再将这种能力赋予每个人,而是赋予大容器(facebook 的例子):
LeftPanel extends JPanel implements LocaleChangeable, RightPanel extends JPanel implements LocaleChangeable 因为它们有带有 text 属性的组件。
这些类现在负责更改其组件的文本。一个伪示例是:
public class LeftPanel extends JPanel implements LocaleChangeable {
private JLabel exitLabel;
@Override
public void localeChanged(Locale newLocale) {
if (newLocale == Locale.ENGLISH) {
exitLabel.setText("Exit");
} else if (newLocale == Locale.GREEK) {
exitLabel.setText("Έξοδος"); //Greek exit
}
}
}
(当然不是一堆if-else条件,而是ResourceBundle逻辑)。
所以...让我们为所有的类容器调用这个方法:
private void broadcastLocaleChange(Locale locale) {
List<Component> components = getChildren(Component.class, myFrame);
components.stream().filter(LocaleChangeable.class::isInstance).map(LocaleChangeable.class::cast)
.forEach(lc -> lc.localeChanged(locale));
}
就是这样!一个完整的例子是:
public class LocaleTest extends JFrame {
private static final long serialVersionUID = 1L;
public LocaleTest() {
super("test");
setDefaultCloseOperation(EXIT_ON_CLOSE);
getContentPane().setLayout(new BorderLayout());
add(new MainPanel());
pack();
setLocationRelativeTo(null);
setVisible(true);
}
private class MainPanel extends JPanel implements LocaleChangeable {
private JLabel label;
private JButton changeLocaleButton;
public MainPanel() {
super(new FlowLayout());
label = new JLabel(Locale.ENGLISH.toString());
add(label);
changeLocaleButton = new JButton("Change Locale");
changeLocaleButton.addActionListener(e -> {
broadcastLocaleChange(Locale.CANADA);
});
add(changeLocaleButton);
}
@Override
public void localeChanged(Locale newLocale) {
label.setText(newLocale.toString());
System.out.println("Language changed.");
}
private void broadcastLocaleChange(Locale locale) {
List<Component> components = getChildren(Component.class, LocaleTest.this);
components.stream().filter(LocaleChangeable.class::isInstance).map(LocaleChangeable.class::cast)
.forEach(lc -> lc.localeChanged(locale));
}
}
private static <T extends Component> List<T> getChildren(Class<T> clazz, final Container container) {
Component[] components;
if (container instanceof JMenu)
components = ((JMenu) container).getMenuComponents();
else
components = container.getComponents();
List<T> compList = new ArrayList<T>();
for (Component comp : components) {
if (clazz.isAssignableFrom(comp.getClass())) {
compList.add(clazz.cast(comp));
}
if (comp instanceof Container)
compList.addAll(getChildren(clazz, (Container) comp));
}
return compList;
}
public static interface LocaleChangeable {
void localeChanged(Locale newLocale);
}
public static void main(String[] args) {
SwingUtilities.invokeLater(() -> new LocaleTest().setVisible(true));
}
}