Java 有什么方法可以自动应用组件吗?

Java 有什么方法可以自动应用组件吗?,java,swing,right-to-left,Java,Swing,Right To Left,如果我想将文本翻转到默认区域设置的适当方向,我可以调用applycomponentation,它将只遍历组件树一次并正确设置方向 问题是,它实际上只做一次。因此,如果您有一个GUI,其中组件被动态添加到树中,那么这些动态添加的组件的方向是错误的 private JPanel makePanel() { JPanel panel = new JPanel(new FlowLayout(FlowLayout.LEADING)); panel.add(new JLabel("First

如果我想将文本翻转到默认区域设置的适当方向,我可以调用
applycomponentation
,它将只遍历组件树一次并正确设置方向

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

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;
}
下面是一个快速、人为的演示程序:

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会在库中触发一些微妙的bug,这些bug使用对UI类名的检查来确定绘制自己的内容的样式。所以这种方法不是很吸引人

有更好的方法吗

编辑:

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

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

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

将组件方向应用于最内部的
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;
}
看一看

我在测试时遇到了这个问题(并发现了这个问题),我没有找到一个完全自动的解决方案,但部分手动解决方案可能会有所帮助

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

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


有关手动解决方案的详细信息,您可以查看(搜索“buildTextScrollerCheckBox”)

有趣的问题+1如问题中所述:“这是不切实际的,不仅因为有很多对话框,还因为团队中的每个开发人员在创建新对话框时都不会考虑这类问题。”好吧,我知道我可以通过为出现的新面板添加侦听器来解决面板问题。这可能足以在一次对话中解决问题。然后问题又回到了“但是我们仍然有很多对话”,而且第二个标签的方向是错误的。尝试向其添加一个图标,并将其对齐方式设置为
SwingConstants.LEADING
,您将看到,即使第二个图标位于第一个的左侧,它的图标也位于文本的左侧,与指定的方向不匹配。为什么不为这种行为创建一个自定义类并替换
new FlowLayout()
使用
新建MyFlowLayout()
例如,
类MyFlowLayout扩展了FlowLayout{public MyFlowLayout(){//orientation-based code goes here}}
也为对话框执行相同类型的自定义。在一个地方保留通用代码是一个很好的设计。在整个应用程序中替换布局将是一个简单的一次性过程,但它会破坏所有GUI构建器表单,并且人们制作的新表单将再次使用正常布局。我必须破解NetBeans,使其使用我们定制的布局…那里的代码很有趣,因为它递归调用,但ApplyComponention已经是递归的了。。。至于您关于必须调用revalidate()的观点,这实际上是错误的。如果您通过普通方式添加组件,Swing将已经为您调用这些(我曾经在调试器中验证过,因为我自己也不确定)。但是如果您有一个组件正在自己进行渲染,则必须调用revalidate()、repaint()等。或者,如果要将组件添加到对话框中,则可能需要打包()。@Trejkaz进行了更多的测试,发现了您指出的错误(感谢您——我的疏忽太严重了)。我更新了答案,我不确定它是否是答案了。。。