有没有办法自动applyComponentOrientation?

时间:2014-06-15 05:49:03

标签: java swing right-to-left

如果我想将文本翻转到默认语言环境的相应方向,我可以调用applyComponentOrientation,它将只运行组件树一次并正确设置方向。

问题是,它确实只做了一次。因此,如果您有一个GUI,其中组件动态添加到树中,则这些动态添加的组件具有错误的方向。

这是一个快速,人为的示范项目:

import java.awt.ComponentOrientation;
import java.awt.FlowLayout;
import java.util.Locale;
import javax.swing.BoxLayout;
import javax.swing.JFrame;
import javax.swing.JLabel;
import javax.swing.JPanel;
import javax.swing.SwingUtilities;
import javax.swing.WindowConstants;

public class OrientationTest implements Runnable {
    public static void main(String[] args) {
        // Deliberately set locale to one with RTL orientation.
        Locale.setDefault(new Locale("ar", "SA"));

        SwingUtilities.invokeLater(new OrientationTest());
    }

    @Override
    public void run() {
        JFrame frame = new JFrame("Test program");
        JPanel contentPane = new JPanel();
        contentPane.setLayout(new BoxLayout(contentPane, BoxLayout.PAGE_AXIS));
        frame.setContentPane(contentPane);
        contentPane.add(makePanel());

        frame.applyComponentOrientation(
            ComponentOrientation.getOrientation(Locale.getDefault()));

        // usually dynamically added later:
        contentPane.add(makePanel());

        frame.pack();
        frame.setDefaultCloseOperation(WindowConstants.DISPOSE_ON_CLOSE);
        frame.setVisible(true);
    }

    private JPanel makePanel() {
        JPanel panel = new JPanel(new FlowLayout(FlowLayout.LEADING));
        panel.add(new JLabel("First"));
        panel.add(new JLabel("Second"));
        return panel;
    }
}

即使它没有上述问题,我仍然需要手动将其添加到我们的应用程序中的每个帧和对话框中。这是不切实际的,不仅因为有很多对话框,而且还因为团队中的每个开发人员在创建新对话框时都不会考虑这种事情。即使我以某种方式介绍了一个正确完成它的FixedJDialog类,也不能保证每个人都会使用它。

它也有点愚蠢,因为每个组件的ComponentUI都会自动为构建中的每个组件设置,而我无需做任何事情,因此您可以认为方向可以使用同样的机制。

这让我想到了一种可能的方法来自动实现这一点 - 编写多个自定义LAF,每个子类化一些其他实际外观。在installDefaults()上,设置组件方向,然后委托给真实UI。

不幸的是,每个LAF都有很多UI类,并不是所有的类都适合允许委派的整洁系统。此外,在某些情况下,使用非标准的LAF会触发库中的细微错误,这些错误会使用对UI类名称的检查来确定绘制自己内容的样式。所以这种方法并不具有吸引力。

有更好的方法吗?

编辑:

我现在在单个对话框中有一个解决方案。

    ContainerListener listener = new ContainerAdapter() {
        @Override
        public void componentAdded(ContainerEvent e) {
            e.getChild().applyComponentOrientation(
                e.getContainer().getComponentOrientation());
            addListenersToTree(e.getChild());
        }
    };
    addListenersToTree(frame);

addListenersToTree只是递归层次结构,在任何地方添加监听器。这是一个肮脏的解决方案,我不知道拥有所有这些额外的监听器的内存成本是多少,但它适用于一个对话框,因此它至少解决了一部分问题。

2 个答案:

答案 0 :(得分:1)

将组件方向应用于最内层JPanel

解决方案1 ​​

private JPanel makePanel() {
    FlowLayout flowLayout = null;
    if (ComponentOrientation.getOrientation(Locale.getDefault()).isLeftToRight()) {
        flowLayout = new FlowLayout(FlowLayout.LEFT);
    } else {
        flowLayout = new FlowLayout(FlowLayout.RIGHT);
    }
    JPanel panel = new JPanel(flowLayout);
    ...
}

解决方案2

private JPanel makePanel() {
    JPanel panel = new JPanel(new FlowLayout(FlowLayout.LEADING));
    panel.applyComponentOrientation(ComponentOrientation.getOrientation(Locale.getDefault()));
    ...
}

如果在应用组件方向后稍后添加组件,这也适用。

private JPanel makePanel() {
    JPanel panel = new JPanel(new FlowLayout(FlowLayout.LEADING));
    panel.add(new JLabel("First"));
    panel.applyComponentOrientation(ComponentOrientation.getOrientation(Locale.getDefault()));
    panel.add(new JLabel("Second"));
    return panel;
}

查看How to Use FlowLayout

答案 1 :(得分:0)

我在测试HVLayout时遇到了这个问题(并发现了这个问题)而我找不到问题的全自动解决方案,但也许部分手动解决方案可以帮助(某人)。

当您动态添加/删除组件时,如果您添加一个组件来执行它自己的渲染(例如JScrollPane),则必须通知Swing更新UI,并且这是通过组合{{revalidate()来完成的。 1}} / repaint()(甚至是对话框中的pack())。在这个"更新UI"过程可以使用父/根容器的组件方向调用applyComponentOrientation(..)方法。
这并没有解决所有当前对话框的问题,如果在添加/删除组件后没有用于更新UI的通用/共享实用程序方法,那么它可能永远不会工作。

但是,正如@Braj发现的那样,当布局管理器配置为使用绝对位置时(例如LEFT而不是LEADING),所有这些都没什么效果。避免使用不使用相对位置的布局管理器。

有关手动解决方案的详细信息,您可以查看test UI source code(搜索" buildTextScrollerCheckBox")。