让 JButtons 在 JFrame 中始终居中高度?

Making JButtons Always Centered Height-Wise in the JFrame?

到目前为止,我已经编写了以下程序来帮助我熟悉 Java Swing GUI 编程。有没有一种方法可以修改我的代码,使两个 JButton 控件(addButtonsubtractButton)始终在 JFrame 中居中高度,因为 window 调整大小了吗?

public class GUITesting extends JFrame implements ActionListener {

private static final int INITIAL_WIDTH = 400;
private static final int INITIAL_HEIGHT = 300;
JButton addButton, subtractButton;

public GUITesting() {
    
    setSize(INITIAL_WIDTH, INITIAL_HEIGHT);
    setLocation(200,200);
    setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
    setTitle("0");
    
    addButton = new JButton("Add");
    addButton.addActionListener(this);
    add(addButton); // Adding Button to the Window Frame Creation
    subtractButton = new JButton("Subtract");
    subtractButton.addActionListener(this);
    
    setLayout(null);
    
    addButton.setSize(INITIAL_WIDTH / 4, INITIAL_HEIGHT / 6);
    subtractButton.setSize(INITIAL_WIDTH / 4, INITIAL_HEIGHT / 6);
    
    addButton.setLocation(INITIAL_WIDTH / 6, INITIAL_HEIGHT / 6);
    subtractButton.setLocation(7 * INITIAL_WIDTH / 12, INITIAL_HEIGHT / 6);
    
    add(addButton);
    add(subtractButton);
    
    addComponentListener(new Resizer());
    addMouseListener(new MouseAdapter() {
        public void mouseClicked(MouseEvent e) {
            int x = e.getX();
            int y = e.getY();
            setTitle("Mouse clicked at (" + x + "," + y + ")");
        }
    });
    
}

public void actionPerformed(ActionEvent event) {
    
    int value = Integer.parseInt(getTitle());
    
    if (event.getActionCommand().equals("Add")) {
        value = value + 1;
    }
    else {
        value = value - 1;
    }
    
}

private class Resizer extends ComponentAdapter {
    
    // Override Some of the Abstract Methods from ComponentAdapter
    public void componentResized(ComponentEvent event) {
        int width = getWidth();
        int height = getHeight();
        addButton.setSize(width / 4, height / 6); 
        subtractButton.setSize(width / 4, height / 6);
        addButton.setLocation(width / 6, height / 6);
        subtractButton.setLocation(7 * width / 12, height / 6);
    }
    
}

public static void main(String[] args) {
    GUITesting window = new GUITesting();
    window.setVisible(true);
}
}

无需做任何特殊操作,您只需使用 GridBagLayout

import java.awt.EventQueue;
import java.awt.GridBagLayout;
import javax.swing.JButton;
import javax.swing.JFrame;
import javax.swing.JPanel;

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

    public Main() {
        EventQueue.invokeLater(new Runnable() {
            @Override
            public void run() {
                JFrame frame = new JFrame();
                frame.add(new TestPane());
                frame.pack();
                frame.setLocationRelativeTo(null);
                frame.setVisible(true);
            }
        });
    }

    public class TestPane extends JPanel {
        public TestPane() {
            setLayout(new GridBagLayout());

            add(new JButton("Add"));
            add(new JButton("Subtract"));
        }
    }
}

有关详细信息,请参阅 Laying Out Components Within a Container and How to Use GridBagLayout

如果您希望按钮大小相同,您可以使用复合布局,同时利用 GridBagLayoutGridLayout,例如...

import java.awt.EventQueue;
import java.awt.GridBagLayout;
import java.awt.GridLayout;
import javax.swing.JButton;
import javax.swing.JFrame;
import javax.swing.JPanel;

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

    public Main() {
        EventQueue.invokeLater(new Runnable() {
            @Override
            public void run() {
                JFrame frame = new JFrame();
                frame.add(new TestPane());
                frame.pack();
                frame.setLocationRelativeTo(null);
                frame.setVisible(true);
            }
        });
    }

    public class TestPane extends JPanel {
        public TestPane() {
            setLayout(new GridBagLayout());

            JPanel contentPane = new JPanel(new GridLayout(1, 2));
            contentPane.add(new JButton("Add"));
            contentPane.add(new JButton("Subtract"));

            add(contentPane);
        }
    }
}

但是,如果您想做一些非常时髦的事情,您可以构建自己的布局管理器,例如 ButtonLayout,例如...

这基本上与第二个示例相同,但提供了一些额外的对齐选项,这些选项比通过 GridBagLayout

更容易实现
import java.awt.Component;
import java.awt.Container;
import java.awt.Dimension;
import java.awt.EventQueue;
import java.awt.LayoutManager2;
import java.awt.Point;
import javax.swing.JButton;
import javax.swing.JFrame;
import javax.swing.JPanel;

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

    public Main() {
        EventQueue.invokeLater(new Runnable() {
            @Override
            public void run() {
                JFrame frame = new JFrame();
                frame.add(new TestPane());
                frame.pack();
                frame.setLocationRelativeTo(null);
                frame.setVisible(true);
            }
        });
    }

    public class TestPane extends JPanel {
        public TestPane() {
            setLayout(new ButtonLayout(ButtonLayout.Alignment.HORIZONTAL, ButtonLayout.Anchor.CENTER));
            add(new JButton("Add"));
            add(new JButton("Subtract"));
        }
    }

    public class ButtonLayout implements LayoutManager2 {

        public enum Alignment {
            VERTICAL, HORIZONTAL
        }

        public enum Anchor {
            LEADING, CENTER, TRAILING
        }

        private Alignment alignment;
        private Anchor anchor;
        private int padding;

        private Dimension virtualBounds;

        public ButtonLayout() {
            this(Alignment.HORIZONTAL, Anchor.TRAILING, 0);
        }

        public ButtonLayout(Alignment alignment, Anchor anchor) {
            this(alignment, anchor, 0);
        }

        public ButtonLayout(Alignment alignment, Anchor anchor, int padding) {
            this.alignment = alignment;
            this.padding = padding;
            this.anchor = anchor;
        }

        public Alignment getAlignment() {
            return alignment;
        }

        public Anchor getAnchor() {
            return anchor;
        }

        protected int getPadding() {
            return padding;
        }

        protected int getTotalPadding(Container parent) {
            int padding = getPadding();
            return (padding * parent.getComponentCount()) - padding;
        }

        @Override
        public void addLayoutComponent(Component comp, Object constraints) {
        }

        @Override
        public void addLayoutComponent(String name, Component comp) {
        }

        @Override
        public void removeLayoutComponent(Component comp) {
        }

        @Override
        public void invalidateLayout(Container target) {
            virtualBounds = null;
        }

        protected Dimension virtualLayout(Container parent) {
            if (virtualBounds != null) {
                return virtualBounds;
            }
            int maxWidth = 0;
            int maxHeight = 0;

            for (Component component : parent.getComponents()) {
                Dimension preferredSize = component.getPreferredSize();
                maxHeight = Math.max(maxHeight, preferredSize.height);
                maxWidth = Math.max(maxWidth, preferredSize.width);
            }

            int padding = 0;
            int width = 0;
            int height = 0;
            int componentCount = parent.getComponentCount();
            switch (alignment) {
                case HORIZONTAL:
                    width = (maxWidth * componentCount) + getTotalPadding(parent);
                    height = maxHeight;
                    break;
                case VERTICAL:
                    width = maxWidth;
                    height = (maxHeight * componentCount) + getTotalPadding(parent);
                    break;
            }

            virtualBounds = new Dimension(width, height);
            return virtualBounds;
        }

        @Override
        public Dimension maximumLayoutSize(Container parent) {
            return virtualLayout(parent);
        }

        @Override
        public Dimension preferredLayoutSize(Container parent) {
            return virtualLayout(parent);
        }

        @Override
        public Dimension minimumLayoutSize(Container parent) {
            return virtualLayout(parent);
        }

        @Override
        public float getLayoutAlignmentX(Container target) {
            return 0.5f;
        }

        @Override
        public float getLayoutAlignmentY(Container target) {
            return 0.5f;
        }

        @Override
        public void layoutContainer(Container parent) {
            int maxWidth = 0;
            int maxHeight = 0;

            for (Component component : parent.getComponents()) {
                Dimension preferredSize = component.getPreferredSize();
                maxHeight = Math.max(maxHeight, preferredSize.height);
                maxWidth = Math.max(maxWidth, preferredSize.width);
            }

            Dimension defaultSize = new Dimension(maxWidth, maxHeight);
            Point point = offsetForAnchor(parent, defaultSize);

            int xDelta = 0;
            int yDelta = 0;
            switch (alignment) {
                case HORIZONTAL:
                    xDelta = getPadding() + defaultSize.width;
                    break;
                case VERTICAL:
                    yDelta = getPadding() + defaultSize.height;
                    break;
            }
            for (Component component : parent.getComponents()) {
                component.setSize(defaultSize);
                component.setLocation(point);
                point = new Point(point.x + xDelta, point.y + yDelta);
            }
        }

        protected Point offsetForAnchor(Container parent, Dimension defaultSize) {
            switch (anchor) {
                case LEADING:
                    return leadingOffSet(parent, defaultSize);
                case TRAILING:
                    return trailingOffSet(parent, defaultSize);
                case CENTER:
                    return centerOffSet(parent, defaultSize);
            }
            return new Point(0, 0);
        }

        protected Point leadingOffSet(Container parent, Dimension defaultSize) {
            Point point = new Point(0, 0);
            switch (alignment) {
                case HORIZONTAL:
                    point.x = padding;
                    point.y = (parent.getHeight() - defaultSize.height) / 2;
                    break;
                case VERTICAL:
                    point.x = (parent.getWidth() - defaultSize.width) / 2;
                    point.y = padding;
                    break;
            }
            return point;
        }

        protected Point trailingOffSet(Container parent, Dimension defaultSize) {
            Point point = new Point(0, 0);
            int componentCount = parent.getComponentCount();
            switch (alignment) {
                case HORIZONTAL:
                    int totalWidth = (defaultSize.width * componentCount) + getTotalPadding(parent);
                    point.x = parent.getWidth() - totalWidth;
                    point.y = (parent.getHeight() - defaultSize.height) / 2;
                    break;
                case VERTICAL:
                    int totalHeight = (defaultSize.height * componentCount) + getTotalPadding(parent);
                    point.x = (parent.getWidth() - defaultSize.width) / 2;
                    point.y = parent.getHeight() - totalHeight;
                    break;
            }
            return point;
        }

        protected Point centerOffSet(Container parent, Dimension defaultSize) {
            Point point = new Point(0, 0);
            int componentCount = parent.getComponentCount();
            switch (alignment) {
                case HORIZONTAL: {
                    int totalWidth = (defaultSize.width * componentCount) + getTotalPadding(parent);
                    point.x = (parent.getWidth() - totalWidth) / 2;
                    point.y = (parent.getHeight() - defaultSize.height) / 2;
                }
                break;
                case VERTICAL: {
                    int totalHeight = (defaultSize.height * componentCount) + getTotalPadding(parent);
                    point.x = (parent.getWidth() - defaultSize.width) / 2;
                    point.y = (parent.getHeight() - totalHeight) / 2;
                }
                break;
            }
            return point;
        }

    }
}