【问题标题】:Is there some way to applyComponentOrientation automatically?有没有办法自动应用组件方向?
【发布时间】:2014-06-15 05:49:02
【问题描述】:

如果我想将文本翻转到默认语言环境的适当方向,我可以调用applyComponentOrientation,它会只遍历一次组件树并正确设置方向。 p>

问题是,它真的只做一次。因此,如果您有一个 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,每个 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 只是递归层次结构,到处添加监听器。这是一个肮脏的解决方案,我不知道拥有所有这些额外侦听器的内存成本是多少,但它适用于一个对话框,因此它至少解决了部分问题。

【问题讨论】:

  • 有趣的问题+1

标签: java swing right-to-left


【解决方案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

【讨论】:

  • 如问题中所述:“这不切实际,不仅因为有很多对话框,而且因为并非团队中的每个开发人员在创建新对话框时都会考虑这种事情。”好吧,我知道我可以通过为出现的新面板添加侦听器来解决面板问题。这可能足以在单个对话中解决问题。然后问题又回到“但我们还有很多对话框”。
  • 另外,第二个标签的方向也不正确。尝试向它添加一个图标并将其对齐设置为SwingConstants.LEADING,您会看到即使 Second 在 First 的左侧,它的图标也在文本的左侧,这与指定的方向不匹配。
  • 为什么不为这种行为创建一个自定义类并将new FlowLayout()替换为new MyFlowLayout(),例如class MyFlowLayout extends FlowLayout{public MyFlowLayout(){//orientation based code goes here}}
  • 对 Dialog 也进行相同类型的自定义。将通用代码保存在一个地方是很好的设计。
  • 替换整个应用程序的布局将是一件容易的事,但它会破坏所有 GUI 构建器表单,并且人们制作的新表单将再次使用正常布局。我必须破解 NetBeans 才能使用我们的自定义布局...
【解决方案2】:

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

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

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

手动解决方案的详细内容可以看test UI source code(搜索“buildTextScrollerCheckBox”)。

【讨论】:

  • 那里的代码很有趣,因为它以递归方式调用,但 applyComponentOrientation 已经是递归的……就您必须调用 revalidate() 的观点而言,这实际上是不正确的。如果您通过正常方式添加组件,Swing 已经为您调用了这些(我曾经在调试器中验证过这一点,因为我自己不确定。)但是如果您有一个正在执行自己的渲染的组件,那么您有调用 revalidate()、repaint() 等。或者,如果您要向对话框中添加一个组件,您可能需要进行 pack()。
  • @Trejkaz 进行了更多测试并发现了您指出的错误(感谢您 - 我的疏忽大意)。我更新了答案,我不确定它是否是答案了......
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 2011-05-04
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2020-10-28
  • 2019-10-15
  • 2010-12-27
相关资源
最近更新 更多