1

有没有办法根据它们在 JPanel 中显示的顺序(从左上到右下)而不是它们添加到 JPanel 的顺序来获取 JPanel 中的组件列表?

这似乎按照它们添加到面板的顺序获取组件

Component[] comps = myJPanel.getComponents();
4

2 回答 2

4

看一下Container#getFocusTraversalPolicy,它返回一个FocusTraversalPolicy具有通过容器确定焦点移动的方法。

这(应该)为您提供组件的自然顺序(从布局管理器和焦点管理器的角度来看)

我会从FocusTraversalPolicy#getFirstComponent(Container)和开始FocusTraversalPolicy#getComponentAfter(Container, Component)

如果这不起作用,您可能需要为自己编写一个自定义Comparator并相应地对组件数组进行排序

更新 - 焦点遍历示例

public class ComponentOrder {

    public static void main(String[] args) {
        new ComponentOrder();
    }

    public ComponentOrder() {
        EventQueue.invokeLater(new Runnable() {
            @Override
            public void run() {
                try {
                    UIManager.setLookAndFeel(UIManager.getSystemLookAndFeelClassName());
                } catch (ClassNotFoundException | InstantiationException | IllegalAccessException | UnsupportedLookAndFeelException ex) {
                }

                BodyPane body = new BodyPane();

                JFrame frame = new JFrame("Testing");
                frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
                frame.setLayout(new BorderLayout());
                frame.add(body);
                frame.pack();
                frame.setLocationRelativeTo(null);
                frame.setVisible(true);

                Container focusRoot = body.getFocusCycleRootAncestor();
                FocusTraversalPolicy ftp = focusRoot.getFocusTraversalPolicy();
                Component comp = ftp.getFirstComponent(body);
                Component first = comp;
                while (comp != null) {
                    System.out.println(" - " + comp);
                    comp = ftp.getComponentAfter(focusRoot, comp);
                    if (comp.equals(first)) {
                        break;
                    }
                }
            }
        });
    }

    public class BodyPane extends JPanel {

        private JTextField fldFirstName;
        private JTextField fldMiddleName;
        private JTextField fldLastName;
        private JTextField fldDateOfBirth;
        private JTextField fldEMail;
        private JButton okButton;
        private JButton cancelButton;

        public BodyPane() {

            setLayout(new BorderLayout());
            add(createFieldsPane());
            add(createButtonsPane(), BorderLayout.SOUTH);

        }

        public JPanel createButtonsPane() {

            JPanel panel = new JPanel(new FlowLayout());
            panel.add((okButton = createButton("Ok")));
            panel.add((cancelButton = createButton("Cancel")));

            return panel;

        }

        protected JButton createButton(String text) {

            return new JButton(text);

        }

        public JPanel createFieldsPane() {

            JPanel panel = new JPanel(new GridBagLayout());
            GridBagConstraints gbc = new GridBagConstraints();
            gbc.insets = new Insets(2, 2, 2, 2);
            gbc.gridx = 0;
            gbc.gridy = 0;
            gbc.anchor = GridBagConstraints.WEST;

            panel.add(createLabel("First Name:"), gbc);
            gbc.gridy++;
            panel.add(createLabel("Middle Name:"), gbc);
            gbc.gridy++;
            panel.add(createLabel("Last Name:"), gbc);
            gbc.gridy++;
            panel.add(createLabel("Date of Birth:"), gbc);
            gbc.gridy++;
            panel.add(createLabel("EMail:"), gbc);

            gbc.gridy = 0;
            gbc.gridx++;
            gbc.weightx = 1;
            panel.add((fldFirstName = createField()), gbc);
            gbc.gridy++;
            panel.add((fldLastName = createField()), gbc);
            gbc.gridy++;
            panel.add((fldMiddleName = createField()), gbc);
            gbc.gridy++;
            panel.add((fldDateOfBirth = createField()), gbc);
            gbc.gridy++;
            panel.add((fldEMail = createField()), gbc);

            JPanel filler = new JPanel();
            filler.setOpaque(false);

            gbc.gridy++;
            gbc.weightx = 1;
            gbc.weighty = 1;
            panel.add(filler, gbc);

            return panel;

        }

        protected JLabel createLabel(String text) {

            return new JLabel(text);

        }

        protected JTextField createField() {

            JTextField field = new JTextField(12);
            return field;

        }
    }
}

比较器示例

以下示例使用比较器来确定组件的流向。此版本将父容器中的所有组件转换为该容器坐标空间,因此应该可以将其与复合容器一起使用。

nb我偷了javax.swing.LayoutComparator受包保护的比较器(这对 SwingTeam 来说很好)并修改它以将组件坐标转换回父坐标空间

public class ComponentOrder {

    public static void main(String[] args) {
        new ComponentOrder();
    }

    public ComponentOrder() {
        EventQueue.invokeLater(new Runnable() {
            @Override
            public void run() {
                try {
                    UIManager.setLookAndFeel(UIManager.getSystemLookAndFeelClassName());
                } catch (ClassNotFoundException | InstantiationException | IllegalAccessException | UnsupportedLookAndFeelException ex) {
                }

                BodyPane body = new BodyPane();

                JFrame frame = new JFrame("Testing");
                frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
                frame.setLayout(new BorderLayout());
                frame.add(body);
                frame.pack();
                frame.setLocationRelativeTo(null);
                frame.setVisible(true);

                List<Component> components = new ArrayList<Component>(25);
                getContents(body, components);
                LayoutComparator lc = new LayoutComparator(body);
                lc.setComponentOrientation(body.getComponentOrientation());
                Collections.sort(components, lc);
                for (Component comp : components) {
                    System.out.println(comp);
                }
            }
        });
    }

    protected void getContents(Container container, List<Component> components) {
        for (Component comp : container.getComponents()) {
            components.add(comp);
            if (comp instanceof Container) {
                getContents((Container) comp, components);
            }
        }
    }

    public class BodyPane extends JPanel {

        private JTextField fldFirstName;
        private JTextField fldMiddleName;
        private JTextField fldLastName;
        private JTextField fldDateOfBirth;
        private JTextField fldEMail;
        private JButton okButton;
        private JButton cancelButton;

        public BodyPane() {
            setLayout(new BorderLayout());
            add(createFieldsPane());
            add(createButtonsPane(), BorderLayout.SOUTH);
        }

        public JPanel createButtonsPane() {
            JPanel panel = new JPanel(new FlowLayout());
            panel.add((okButton = createButton("Ok")));
            panel.add((cancelButton = createButton("Cancel")));
            return panel;
        }

        protected JButton createButton(String text) {
            return new JButton(text);
        }

        public JPanel createFieldsPane() {
            JPanel panel = new JPanel(new GridBagLayout());
            GridBagConstraints gbc = new GridBagConstraints();
            gbc.insets = new Insets(2, 2, 2, 2);
            gbc.gridx = 0;
            gbc.gridy = 0;
            gbc.anchor = GridBagConstraints.WEST;

            panel.add(createLabel("First Name:"), gbc);
            gbc.gridy++;
            panel.add(createLabel("Middle Name:"), gbc);
            gbc.gridy++;
            panel.add(createLabel("Last Name:"), gbc);
            gbc.gridy++;
            panel.add(createLabel("Date of Birth:"), gbc);
            gbc.gridy++;
            panel.add(createLabel("EMail:"), gbc);

            gbc.gridy = 0;
            gbc.gridx++;
            gbc.weightx = 1;
            panel.add((fldFirstName = createField("FirstName")), gbc);
            gbc.gridy++;
            panel.add((fldLastName = createField("LastName")), gbc);
            gbc.gridy++;
            panel.add((fldMiddleName = createField("MiddleName")), gbc);
            gbc.gridy++;
            panel.add((fldDateOfBirth = createField("DateOfBirth")), gbc);
            gbc.gridy++;
            panel.add((fldEMail = createField("EMail")), gbc);

            JPanel filler = new JPanel();
            filler.setOpaque(false);

            gbc.gridy++;
            gbc.weightx = 1;
            gbc.weighty = 1;
            panel.add(filler, gbc);

            return panel;
        }

        protected JLabel createLabel(String text) {
            JLabel label = new JLabel(text);
            label.setName(text);
            return label;
        }

        protected JTextField createField(String name) {
            JTextField field = new JTextField(12);
            field.setName("Field-" + name);
            return field;
        }
    }

    public class LayoutComparator implements Comparator<Component>, java.io.Serializable {

        private static final int ROW_TOLERANCE = 10;
        private boolean horizontal = true;
        private boolean leftToRight = true;

        private Component parent;

        public LayoutComparator(Component parent) {
            this.parent = parent;
        }

        void setComponentOrientation(ComponentOrientation orientation) {
            horizontal = orientation.isHorizontal();
            leftToRight = orientation.isLeftToRight();
        }

        public int compare(Component a, Component b) {
            if (a == b) {
                return 0;
            }

            // Row/Column algorithm only applies to siblings. If 'a' and 'b'
            // aren't siblings, then we need to find their most inferior
            // ancestors which share a parent. Compute the ancestory lists for
            // each Component and then search from the Window down until the
            // hierarchy branches.
            if (a.getParent() != b.getParent()) {
                LinkedList<Component> aAncestory = new LinkedList<Component>();

                for (; a != null; a = a.getParent()) {
                    aAncestory.add(a);
                    if (a instanceof Window) {
                        break;
                    }
                }
                if (a == null) {
                    // 'a' is not part of a Window hierarchy. Can't cope.
                    throw new ClassCastException();
                }

                LinkedList<Component> bAncestory = new LinkedList<Component>();

                for (; b != null; b = b.getParent()) {
                    bAncestory.add(b);
                    if (b instanceof Window) {
                        break;
                    }
                }
                if (b == null) {
                    // 'b' is not part of a Window hierarchy. Can't cope.
                    throw new ClassCastException();
                }

                for (ListIterator<Component> aIter = aAncestory.listIterator(aAncestory.size()),
                                bIter = bAncestory.listIterator(bAncestory.size());;) {
                    if (aIter.hasPrevious()) {
                        a = aIter.previous();
                    } else {
                        // a is an ancestor of b
                        return -1;
                    }

                    if (bIter.hasPrevious()) {
                        b = bIter.previous();
                    } else {
                        // b is an ancestor of a
                        return 1;
                    }

                    if (a != b) {
                        break;
                    }
                }
            }

            Point pa = SwingUtilities.convertPoint(a, a.getLocation(), parent);
            Point pb = SwingUtilities.convertPoint(b, b.getLocation(), parent);

            int ax = pa.x, ay = pa.y, bx = pb.x, by = pb.y;

            int zOrder = a.getParent().getComponentZOrder(a) - b.getParent().getComponentZOrder(b);
            if (horizontal) {
                if (leftToRight) {

                    // LT - Western Europe (optional for Japanese, Chinese, Korean)

                    if (Math.abs(ay - by) < ROW_TOLERANCE) {
                        return (ax < bx) ? -1 : ((ax > bx) ? 1 : zOrder);
                    } else {
                        return (ay < by) ? -1 : 1;
                    }
                } else { // !leftToRight

                    // RT - Middle East (Arabic, Hebrew)

                    if (Math.abs(ay - by) < ROW_TOLERANCE) {
                        return (ax > bx) ? -1 : ((ax < bx) ? 1 : zOrder);
                    } else {
                        return (ay < by) ? -1 : 1;
                    }
                }
            } else { // !horizontal
                if (leftToRight) {

                    // TL - Mongolian

                    if (Math.abs(ax - bx) < ROW_TOLERANCE) {
                        return (ay < by) ? -1 : ((ay > by) ? 1 : zOrder);
                    } else {
                        return (ax < bx) ? -1 : 1;
                    }
                } else { // !leftToRight

                    // TR - Japanese, Chinese, Korean

                    if (Math.abs(ax - bx) < ROW_TOLERANCE) {
                        return (ay < by) ? -1 : ((ay > by) ? 1 : zOrder);
                    } else {
                        return (ax > bx) ? -1 : 1;
                    }
                }
            }
        }
    }
}
于 2012-12-02T22:28:50.930 回答
0

虽然之前提交的解决方案很巧妙,但(总体上)满足我大部分要求的方法相当简单,并且依赖于窗口架构的左上角有零零(根)点。

由于确切的编码将取决于您选择使用哪些库,让我试探性地描述随后的过程:

任意一点到原点的距离(x,y)(x^2 + y^2)**(0.5)。使用这个可以构造一个影子阵列,它用一个数字来镜像每一对。通过简单地对这些距离中的每一个进行关系升序排序,可以得出从左上到右下顺序的相当好的近似值。

有一些病态的例子应该避免(或至少理解)。例如,使用指向屏幕左上角的指南针将生成无限数量的点,这些点与原点的距离完全相同。

为了解决这个问题,可以简单地用一个乘数对xy值进行预缩放,然后再对其进行平方并将其与所描述的另一个平方单位相结合。这将按比例"y"为高于x窗口左边缘的 a 提供更多价值,反之亦然。

于 2013-04-13T21:59:53.797 回答