底部面板的垂直对齐

Vertical alignment of a bottom panel

我想在底部面板中垂直对齐 3 个按钮。

这是我写的:

ClientWindow(){
    pickBtn = new JButton();
    attackBtn = new JButton();
    placeBtn = new JButton();
    
    JPanel userPanel = new JPanel();
    userPanel.setPreferredSize(new Dimension(100,100));
    userPanel.setBackground(Color.red);
    
    JFrame frame = new JFrame();
    frame.setTitle("Test");
    frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
    frame.setLayout(new BorderLayout());
    frame.setResizable(false);
    frame.setSize(1280,720);
    frame.setLocationRelativeTo(null);
    
    frame.add(userPanel,BorderLayout.SOUTH);
    
    userPanel.add(pickBtn);
    userPanel.add(attackBtn);
    userPanel.add(placeBtn);
    
    frame.setVisible(true);
}

如何垂直对齐它们?

看看 在容器中布置组件

例如,以下使用 GridBagLayout

import java.awt.BorderLayout;
import java.awt.EventQueue;
import java.awt.GridBagConstraints;
import java.awt.GridBagLayout;
import javax.swing.JButton;
import javax.swing.JFrame;
import javax.swing.JLabel;
import javax.swing.JPanel;
import javax.swing.border.EmptyBorder;

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

    public Main() {
        EventQueue.invokeLater(new Runnable() {
            @Override
            public void run() {
                JLabel label = new JLabel("This is just here to make some content");
                label.setBorder(new EmptyBorder(32, 32, 32, 32));

                JFrame frame = new JFrame();
                frame.add(label);
                frame.add(new UserPanel(), BorderLayout.SOUTH);
                frame.pack();
                frame.setLocationRelativeTo(null);
                frame.setVisible(true);
            }
        });
    }

    public class UserPanel extends JPanel {

        public UserPanel() {
            JButton pickBtn = new JButton("Pick");
            JButton attackBtn = new JButton("Attack");
            JButton placeBtn = new JButton("Place");

            setLayout(new GridBagLayout());
            GridBagConstraints gbc = new GridBagConstraints();
            gbc.gridwidth = GridBagConstraints.REMAINDER;
            gbc.fill = GridBagConstraints.HORIZONTAL;

            add(pickBtn, gbc);
            add(attackBtn, gbc);
            add(placeBtn, gbc);
        }
    }
}

做一个ButtonLayout

重要

请注意:以下内容旨在替换上面示例中的 GridBagLayout,因为 GridBagLayout 很复杂,为此目的可能有点矫枉过正


很久以前,我可以理解 ButtonLayout 的一个非常简洁的概念,它基本上提供了一个简单的布局管理器来布局按钮,类似于大多数 OS 所做的(即,按钮大小相同)。

以下是该概念的一个非常基本的示例。

import java.awt.Color;
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;
import javax.swing.border.LineBorder;

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 UserPanel());
                frame.setSize(400, 400);
                frame.setLocationRelativeTo(null);
                frame.setVisible(true);
            }
        });
    }

    public class UserPanel extends JPanel {

        public UserPanel() {
            JButton pickBtn = new JButton("Pick");
            JButton attackBtn = new JButton("Attack");
            JButton placeBtn = new JButton("Place");

            setBorder(new LineBorder(Color.BLACK));
            setLayout(new ButtonLayout(ButtonLayout.Alignment.VERTICAL, ButtonLayout.Anchor.TRAILING));
            add(pickBtn);
            add(attackBtn);
            add(placeBtn);
        }
    }

    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;
        }

    }
}