如何制作四象限Java的Swing GUI,其边总是符合黄金比例?

How to make 4 quadrant Java Swing GUI whose sides always conforms to the golden ratio?

我想要一个有 4 个象限的矩形 GUI。 JFrame(不包括 minimize/maximize/close 按钮)的高度与宽度之比应为 1 比 phi,其中 phi 等于黄金比例(大约 1.62)。

现在,对于 JFrame 内的四个象限。 JFrame 有一条水平分界线,将高度分为上下两部分。上层高度与下层高度之比也应为φ比一。最后,JFrame 有一条垂直分界线,将宽度分成左右两部分。左侧和右侧之间的比率也应该是 1 比 phi。见上图。

现在,这是困难的部分。无论将哪个 Swing 组件(JScrollPane、JList、JTextArea、JPanel、JTree 或 JButton)放入相应的网格位置,我都希望这四个组件始终遵循比例。例如,我希望能够从四个网格位置中的四个 JButton 开始,然后将其中一个 JPanel 与其中具有 JList 的 JScrollPane 交换,而内部组件不会改变外部组件的相对比例,即使内部组件中有一些文本或数据或其他内容。见下图。

无论我多么努力地实现它(使用 GridBadLayout 和一组约束),我都无法让网格线保持不变。如何让网格线保持原位?

到目前为止,我的源代码如下所示:

public static final double GOLDEN_RATIO = 1.6180339887498948482;
public static final double RELATIVE_LENGTH_OF_LONGER_SIDE = 1 / GOLDEN_RATIO;
public static final double RELATIVE_LENGTH_OF_SHORTER_SIDE = 1 - (1/GOLDEN_RATIO);

// ...

    // make GridBagLayout
    pane.setLayout(new GridBagLayout());
    final GridBagConstraints c = new GridBagConstraints();

    // Make 4 components to put in the four grid spaces.
    JButton filler1 = new JButton();
    c.fill = GridBagConstraints.BOTH;
    c.weightx = RELATIVE_LENGTH_OF_SHORTER_SIDE;
    c.weighty = RELATIVE_LENGTH_OF_LONGER_SIDE;
    c.gridx = 0;
    c.gridy = 0;
    filler1.setMinimumSize(new Dimension(0,0));
    filler1.setPreferredSize(new Dimension(0,0));
    pane.add(filler1, c);

    JButton filler2 = new JButton();
    c.fill = GridBagConstraints.BOTH;
    c.weightx = RELATIVE_LENGTH_OF_LONGER_SIDE;
    c.weighty = RELATIVE_LENGTH_OF_LONGER_SIDE;
    c.gridx = 1;
    c.gridy = 0;
    filler2.setMinimumSize(new Dimension(0,0));
    filler2.setPreferredSize(new Dimension(0,0));
    pane.add(filler2, c);

    JButton filler3 = new JButton();
    c.fill = GridBagConstraints.BOTH;
    c.weightx = RELATIVE_LENGTH_OF_SHORTER_SIDE;
    c.weighty = RELATIVE_LENGTH_OF_SHORTER_SIDE;
    c.gridx = 0;
    c.gridy = 1;
    filler3.setMinimumSize(new Dimension(0,0));
    filler3.setPreferredSize(new Dimension(0,0));
    pane.add(filler3, c);

    JButton filler4 = new JButton();
    c.fill = GridBagConstraints.BOTH;
    c.weightx = RELATIVE_LENGTH_OF_LONGER_SIDE;
    c.weighty = RELATIVE_LENGTH_OF_SHORTER_SIDE;
    c.gridx = 1;
    c.gridy = 1;
    filler4.setMinimumSize(new Dimension(0,0));
    filler4.setPreferredSize(new Dimension(0,0));
    pane.add(filler4, c);

    // Set the size of the enclosing panel.
    this.setPreferredSize(new Dimension(
            (int)(screen_height_*RELATIVE_LENGTH_OF_LONGER_SIDE), 
            (int)(screen_height_*RELATIVE_LENGTH_OF_SHORTER_SIDE))
    );

此外,无论我把尺寸做得多小,它都不应该看起来像这样:

您应该创建自己的 LayoutManager。我认为您真正需要实现的唯一方法是 layoutContainer。在此方法中,您可以使用指定的比率设置子组件(应为 4)

您可以使用 GroupLayout 为您完成工作。

编辑:添加了交换按钮

示例:

import java.awt.BorderLayout;
import java.awt.Dimension;
import java.awt.EventQueue;
import java.awt.event.ActionListener;
import javax.swing.GroupLayout;
import javax.swing.JButton;
import javax.swing.JFrame;
import javax.swing.JPanel;
import javax.swing.JScrollPane;
import javax.swing.JTree;
import javax.swing.WindowConstants;

public class Test extends JFrame {

    public static final double GOLDEN_RATIO = 1.6180339887498948482;
    public static final double RELATIVE_LENGTH_OF_LONGER_SIDE = 1 / GOLDEN_RATIO;
    public static final double RELATIVE_LENGTH_OF_SHORTER_SIDE = 1 - (1 / GOLDEN_RATIO);
    private static final int screenHeight = 500;
    private static final int LENGTH_OF_LONGER_SIDE_FOR_RATIO = (int) (screenHeight * RELATIVE_LENGTH_OF_LONGER_SIDE);
    private static final int LENGTH_OF_SHORTER_SIDE_FOR_RATIO = (int) (screenHeight * RELATIVE_LENGTH_OF_SHORTER_SIDE);
    private static final int MIN_LENGTH_OF_LONGER_SIDE_FOR_RATIO = (int) (50 * RELATIVE_LENGTH_OF_LONGER_SIDE);
    private static final int MIN_LENGTH_OF_SHORTER_SIDE_FOR_RATIO = (int) (50 * RELATIVE_LENGTH_OF_SHORTER_SIDE);

    public Test() {
        buildGUI();
        setDefaultCloseOperation(WindowConstants.EXIT_ON_CLOSE);
        setLocationRelativeTo(null);
    }

    private void buildGUI() {
        JPanel pane = new JPanel();
        // make GridBagLayout
        GroupLayout layout = new GroupLayout(pane);
        pane.setLayout(layout);

        // Make 4 components to put in the four grid spaces.
        JButton filler1 = new JButton("Press here to swap");
        JButton filler2 = new JButton("Press here to swap");
        JButton filler3 = new JButton("Press here to swap");
        Object[] objects = new Object[50];
        for (int i = 0; i < 50; i++) {
            objects[i] = "Test" + i;
        }
        JTree jTree = new JTree(objects);
        JScrollPane scrollPane = new JScrollPane(jTree);
        JButton button = new JButton("Press here to swap");
        JPanel filler4 = new JPanel(new BorderLayout());
        filler4.add(button);
        ActionListener l = (e) -> {
            if (filler4.getComponents()[0] instanceof JButton) {
                filler4.remove(button);
                filler4.add(scrollPane, BorderLayout.CENTER);
            } else {
                filler4.remove(scrollPane);
                filler4.add(button, BorderLayout.CENTER);
            }
            filler4.repaint();
            filler4.revalidate();
        };
        filler1.addActionListener(l);
        filler2.addActionListener(l);
        filler3.addActionListener(l);
        button.addActionListener(l);
        layout.setVerticalGroup(layout.createSequentialGroup()
                .addGroup(layout.createParallelGroup(GroupLayout.Alignment.LEADING)
                        .addComponent(filler1, MIN_LENGTH_OF_LONGER_SIDE_FOR_RATIO, LENGTH_OF_LONGER_SIDE_FOR_RATIO, Short.MAX_VALUE)
                        .addComponent(filler2, MIN_LENGTH_OF_LONGER_SIDE_FOR_RATIO, LENGTH_OF_LONGER_SIDE_FOR_RATIO, Short.MAX_VALUE)
                )
                .addGroup(layout.createParallelGroup(GroupLayout.Alignment.LEADING)
                        .addComponent(filler3, MIN_LENGTH_OF_SHORTER_SIDE_FOR_RATIO, LENGTH_OF_SHORTER_SIDE_FOR_RATIO, Short.MAX_VALUE)
                        .addComponent(filler4, MIN_LENGTH_OF_SHORTER_SIDE_FOR_RATIO, LENGTH_OF_SHORTER_SIDE_FOR_RATIO, Short.MAX_VALUE)
                ));
        layout.setHorizontalGroup(layout.createSequentialGroup()
                .addGroup(layout.createParallelGroup(GroupLayout.Alignment.LEADING)
                        .addComponent(filler1, MIN_LENGTH_OF_SHORTER_SIDE_FOR_RATIO, LENGTH_OF_SHORTER_SIDE_FOR_RATIO, Short.MAX_VALUE)
                        .addComponent(filler3, MIN_LENGTH_OF_SHORTER_SIDE_FOR_RATIO, LENGTH_OF_SHORTER_SIDE_FOR_RATIO, Short.MAX_VALUE)
                )
                .addGroup(layout.createParallelGroup(GroupLayout.Alignment.LEADING)
                        .addComponent(filler2, MIN_LENGTH_OF_LONGER_SIDE_FOR_RATIO, LENGTH_OF_LONGER_SIDE_FOR_RATIO, Short.MAX_VALUE)
                        .addComponent(filler4, MIN_LENGTH_OF_LONGER_SIDE_FOR_RATIO, LENGTH_OF_LONGER_SIDE_FOR_RATIO, Short.MAX_VALUE)
                ));
        add(pane);
        setSize(new Dimension(200, 200));
    }

    public static void main(String[] args) {
        EventQueue.invokeLater(() -> {
            new Test().setVisible(true);
        });
    }

}