Swing元素在运行时的刷新语言

问题描述 投票:0回答:1

当用户从ResourceBundle中选择一个时,[My Java 8 / Swing应用程序使用.properties和几个JComboBox文件来更改语言:

public static ResourceBundle resourceBundle;
private JComboBox<Locale> comboBox;
private JLabel myLabel;

public Main() {
    //More GUI setup here
    resourceBundle = ResourceBundle.getBundle("Bundle", Locale.ENGLISH); //Set first/default language
    comboBox = new JComboBox<Locale>();
    comboBox.addItem(Locale.ENGLISH);
    comboBox.addItem(Locale.GERMAN);
    comboBox.addItem(Locale.FRENCH);

    myLabel = new JLabel(resourceBundle.getString("myLabelText"));
    myLabel.setFont(new Font("Tahoma", Font.PLAIN, 14));
}

ActionListener上的JComboBox调用此命令,这会立即更改所有GUI元素的语言:

private void changeLanguage() {
    Locale locale = comboBox.getItemAt(comboBox.getSelectedIndex());
    resourceBundle = ResourceBundle.getBundle("Bundle", locale);
    myLabel.setText(resourceBundle.getString("myLabelText"));
}

我的应用程序中有更多的标签和按钮通过changeLanguage设置了语言(尽管并非所有人都使用本地化,例如带有房屋图标的“ home”按钮),我打算添加更多标签和按钮。随着GUI项目数量的增加,忘记添加功能也变得更加容易,这就是为什么我的问题是:

是否有一种方法可以“注册” JLabel,...及其在某个类中的键(直接在创建后),然后更改语言(通过加载另一个Locale),也会自动更改JLabel,...?是否有一种与我的做法不同的常用方法?

java swing internationalization properties-file resourcebundle
1个回答
0
投票

我最近遇到了这个问题,所以我将分享我尝试过的方法以及对我有用的方法。请注意,我还需要在运行时实施更改字体操作。

在Swing应用程序中,我们通常为核心容器扩展容器类。假设我们要为桌面创建Facebook。有人可以创建3个核心类(扩展JPanel或JScrollPane)。假设:LeftPanel extends JPanelMiddlePanel extends JPanelRightPanel extends JPanel。左侧面板代表左侧菜单,中间面板代表主滚动视图,最后右侧面板代表广告区域。当然,此面板中的每个面板都将继承JPanel(可能其中一些面板也将具有新的类,例如PostPanelCommentSectionPanel等)

现在,假设您已阅读The Use of Multiple JFrames: Good or Bad Practice?,则您的应用程序仅使用一个JFrame,并且承载其中的每个组件。甚至模式JDialog都基于它(将其作为其父对象)。因此,您可以将其保留在Singleton之类的位置。

为了更改组件文本,我们将不得不为每个组件调用setText。调用JFramegetComponents将为我们提供其所有组件。如果其中之一是容器,我们将不得不为其调用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.classmyJFrame调用此方法将为您提供JFrame具有的所有组件。

现在,我们必须分开其中哪些可以“刷新”(更改语言)而哪些则不能。让我们为此创建一个Interface

public static interface LocaleChangeable {
    void localeChanged(Locale newLocale);
}

现在,我们不再将其赋予每个容器这种功能,而是将其提供给大型容器(以Facebook为例):LeftPanel extends JPanel implements LocaleChangeableRightPanel extends JPanel implements LocaleChangeable,因为它们具有带有text属性的组件。

这些类现在负责更改其组件的文本。一个pseduo示例将是:

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));
    }
}
© www.soinside.com 2019 - 2024. All rights reserved.