有 Java 编程相关的问题?

你可以在下面搜索框中键入要查询的问题!

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

我的Java 8/Swing应用程序使用一个ResourceBundle和两个.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));
}

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

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

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

有没有办法“注册”一个JLabel,。。。它对某个类的键(直接在创建之后)以及随后更改语言(通过加载另一个Locale)也会自动更改JLabel的文本,。。。?是否有一种常用的方法与我的做法不同


共 (2) 个答案

  1. # 1 楼答案

    我的结局是:

    class ComponentInfo {
        JComponent component;
        String key;
    
        public ComponentInfo(JComponent c,String k) {
            component = c;
            key = k;
        }
    
        public JComponent getComponent() {return component;}
        public String getKey() {return key;}
    }
    

    要使用此方法,请创建一个ArrayList,并在创建后直接向其中添加每个应更改其语言的组件:

    List<ComponentInfo> allComponentInfos = new ArrayList<ComponentInfo>();
    JLabel label_pw = new JLabel(resourceBundle.getString("pw_key"));
    label_pw.setFont(new Font("Tahoma", Font.PLAIN, 14));
    allComponentInfos.add(new ComponentInfo(label_pw, "pw_key"));
    

    通过迭代列表更改语言:

    private void changeLanguage() {
        Locale locale = comboBox_language.getItemAt(comboBox_language.getSelectedIndex());
        resourceBundle = ResourceBundle.getBundle("Bundle", locale);
    
        for(ComponentInfo c:allComponentInfos) {
            if(c.getComponent() instanceof JLabel) {
                ((JLabel) c.getComponent()).setText(resourceBundle.getString(c.getKey()));
            } else if(c.getComponent() instanceof JButton) {
                ((JButton) c.getComponent()).setText(resourceBundle.getString(c.getKey()));
            }
        }
    }
    

    我知道这会为每个组件创建一个额外的对象。:/不幸的是,这是我发现的唯一可以让我只改变JLabel的语言,JButton的语言,。。。这需要改变他们的语言,而不需要在一个方法中手动列出所有这些语言(可能忘记一个)

  2. # 2 楼答案

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

    在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将为我们提供它的所有组件。如果其中一个是container,我们将不得不为它调用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
            }
        }
    }
    

    (当然,ResourceBundle逻辑将发生,而不是一堆if-else条件)

    所以。。。让我们为所有类容器调用此方法:

    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));
        }
    }