为什么即使在设置了 JPanels 的大小后我的框布局也会改变大小

Why does my box Layout change size even after setting Sizes of JPanels

我一直在尝试调整我的 JPanel 大小,同时使用 LayoutManager(例如 BoxLayout)以特定顺序在 Layout 中排列组件,我通过 NOT USING[= 实现了我想要的外观39=] 盒子布局管理器。

这张图片我想要的原创想法:

但我想在为面板使用 Box Layout Manager 时实现这一点,当我这样做时,我得到了这个输出:

添加 BoxLayout 时得到的输出:

这是代码:

AppPanel.java :

import javax.swing.*;
import java.awt.*;

public class AppPanel extends JPanel {

int Width, Height;
MenuButton mb;

AppPanel(int width, int height)
{
    this.Width = width;
    this.Height = height;
    this.setMinimumSize(new Dimension(this.Width/ 4, this.Height));
    this.setPreferredSize(new Dimension(3 * this.Width/ 4, this.Height));
    this.setMaximumSize(new Dimension(this.Width/ 4, this.Height));
    this.setLayout(new BoxLayout(this,BoxLayout.Y_AXIS));
    this.setBackground(Color.GREEN);

    this.mb = new MenuButton("HELLO",this.getPreferredSize());
    this.add(mb);
}

public void update(int width, int height){
    this.Width = width;
    this.Height = height;
    this.setPreferredSize(new Dimension(3 * this.Width/ 4, this.Height));
    //        this.mb.update(this.mb.getGraphics());
    }
}

MenuPanel.java:

import javax.swing.*;
import java.awt.*;

public class MenuPanel extends JPanel {

JLabel l_AppMenu;
int Width, Height;
MenuButton mb;

MenuPanel(int width, int height)
{
    this.Width = width;
    this.Height = height;
    this.setMinimumSize(new Dimension(this.Width/ 4, this.Height));
    this.setPreferredSize(new Dimension(this.Width/ 4, this.Height));
    this.setMaximumSize(new Dimension(this.Width/ 4, this.Height));
    this.setLayout(new BoxLayout(this,BoxLayout.Y_AXIS));
    this.setBackground(Color.BLACK);

    this.mb = new MenuButton("HELLO 1",this.getPreferredSize());
    l_AppMenu = new JLabel("App Menu");

    l_AppMenu.setForeground(Color.GRAY);
    this.add(l_AppMenu);
    this.add(mb);
}

public void update(int width, int height){
    this.Width = width;
    this.Height = height;
    this.setPreferredSize(new Dimension(3 * this.Width/ 4, this.Height));
    //        this.mb.update(this.mb.getGraphics());
    }
}

MainFrame.java:

import javax.swing.*;
import java.awt.event.ComponentEvent;
import java.awt.event.ComponentListener;

public class MainFrame extends JFrame implements ComponentListener {

int Width, Height;
MenuPanel mp;
AppPanel ap;

public MainFrame()
{
    this.Width = 1600;
    this.Height = 900;

    mp = new MenuPanel(this.Width, this.Height);
    ap = new AppPanel(this.Width, this.Height);

    this.setSize(this.Width, this.Height);
    this.setVisible(true);
    this.setTitle("ToolKit");
    this.setDefaultCloseOperation(this.EXIT_ON_CLOSE );

    this.add(mp);
    this.add(ap);
    addComponentListener(this);

    this.getContentPane().setLayout(new     BoxLayout(this.getContentPane(),BoxLayout.X_AXIS));
}

public static void main(String[] args)
{
    MainFrame m = new MainFrame();
}

@Override
public void componentResized(ComponentEvent componentEvent) {
    System.out.println(this.getWidth() + " " + this.getHeight());
    mp.update(this.getWidth(), this.getHeight());
    ap.update(this.getWidth(), this.getHeight());
//        this.setSize(this.getHeight() * 16 / 9 , this.getHeight());
}

@Override
public void componentMoved(ComponentEvent componentEvent) {

}

@Override
public void componentShown(ComponentEvent componentEvent) {

}

@Override
public void componentHidden(ComponentEvent componentEvent) {

}
}

MenuButton.java:

import javax.swing.*;
import java.awt.*;
import java.awt.event.MouseAdapter;
import java.awt.event.MouseEvent;
import java.awt.event.MouseListener;

public class MenuButton extends JButton implements MouseListener {

String Text;
int Entered = 0;
int Pressed = 0;

MenuButton(String str, Dimension d)
{
    super(str);
    this.Text = str;
    addMouseListener(this);
    this.setMinimumSize(new Dimension((int) d.getWidth() - 20,40));
    this.setMaximumSize(new Dimension((int) d.getWidth() - 20,40));
    this.setPreferredSize(new Dimension((int) d.getWidth() - 20,40));
    this.setBorder(null);
}

public void paintComponent(Graphics g)
{
    if(this.Pressed == 0) {
        g.setColor(new Color(182, 25, 25));
        g.fillRect(0, 0, this.getWidth(), this.getHeight());

        g.setColor(new Color(255, 107, 107));
        g.fillRect(10, 10, this.getWidth() - 20, this.getHeight() - 20);
    }
    else{
        g.setColor(new Color(255, 107, 107));
        g.fillRect(0, 0, this.getWidth(), this.getHeight());

        g.setColor(new Color(255, 107, 107));
        g.fillRect(10, 10, this.getWidth() - 20, this.getHeight() - 20);
    }

    if(this.Entered == 0) {
        g.setColor(new Color(253, 210, 199));
        g.drawString(this.Text, this.getWidth() / 2 - g.getFontMetrics().stringWidth(this.Text) / 2, 25);
    }
    else{
        g.setColor(new Color(1, 36, 67));
        g.drawString(this.Text, this.getWidth() / 2 - g.getFontMetrics().stringWidth(this.Text) / 2, 25);
    }
}

@Override
public void mouseClicked(MouseEvent mouseEvent) {

}

@Override
public void mousePressed(MouseEvent mouseEvent) {
    this.Pressed = 1;
}

@Override
public void mouseReleased(MouseEvent mouseEvent) {
    this.Pressed = 0;
}

@Override
public void mouseEntered(MouseEvent mouseEvent) {
    this.Entered = 1;
}

@Override
public void mouseExited(MouseEvent mouseEvent) {
    this.Entered = 0;
}
}

通过阅读 MenuButton 的源代码,我意识到它根据鼠标事件绘制边框和背景。您不必自己执行此操作,因为 JButtons 支持 Borders 和背景颜色。因此,您可以创建一个 MouseListener 来更新这些属性,并让它用这些属性重新绘制自身(我将在下面的代码示例中进行演示)。

无论如何,回到你的主要问题,这里是你如何做到的,只使用 BoxLayouts:

import java.awt.Color;
import java.awt.Component;
import java.awt.Dimension;
import java.awt.event.ActionListener;
import java.awt.event.MouseAdapter;
import java.awt.event.MouseEvent;
import java.util.Objects;
import javax.swing.BorderFactory;
import javax.swing.Box;
import javax.swing.BoxLayout;
import javax.swing.JButton;
import javax.swing.JComponent;
import javax.swing.JFrame;
import javax.swing.JLabel;
import javax.swing.JPanel;
import javax.swing.SwingUtilities;
import javax.swing.border.Border;

public class BoxLayoutTest {
    
    public static final class MainButtonAdapter extends MouseAdapter {
        
        private final Color fgOnEntered, fgOnNotEntered, bgOnPressed, bgOnNotPressed;
        private final Border onPressed, onNotPressed;

        public MainButtonAdapter(final Color fgOnEntered,
                                 final Color fgOnNotEntered,
                                 final Color bgOnPressed,
                                 final Color bgOnNotPressed,
                                 final Border onPressed,
                                 final Border onNotPressed) {
            this.fgOnEntered = Objects.requireNonNull(fgOnEntered);
            this.fgOnNotEntered = Objects.requireNonNull(fgOnNotEntered);
            this.bgOnPressed = Objects.requireNonNull(bgOnPressed);
            this.bgOnNotPressed = Objects.requireNonNull(bgOnNotPressed);
            this.onPressed = Objects.requireNonNull(onPressed);
            this.onNotPressed = Objects.requireNonNull(onNotPressed);
        }
        
        private void inside(final Component src) {
            if (src != null) {
                src.setForeground(fgOnEntered);
                src.repaint();
            }
        }

        private void released(final Component src) {
            if (src != null) {
                if (src instanceof JComponent)
                    ((JComponent) src).setBorder(onNotPressed);
                src.setBackground(bgOnNotPressed);
                src.repaint();
            }
        }

        //@Override
        //public void mouseWheelMoved(final MouseWheelEvent e) {
        //    inside(e.getComponent());
        //}
        //
        //@Override
        //public void mouseDragged(final MouseEvent e) {
        //    inside(e.getComponent());
        //}

        @Override
        public void mouseMoved(final MouseEvent e) {
            inside(e.getComponent());
        }

        @Override
        public void mouseClicked(final MouseEvent e) {
            released(e.getComponent());
        }

        @Override
        public void mousePressed(final MouseEvent e) {
            final Component src = e.getComponent();
            if (src != null) {
                if (src instanceof JComponent)
                    ((JComponent) src).setBorder(onPressed);
                src.setBackground(bgOnPressed);
                src.repaint();
            }
        }

        @Override
        public void mouseReleased(final MouseEvent e) {
            released(e.getComponent());
        }

        @Override
        public void mouseEntered(final MouseEvent e) {
            inside(e.getComponent());
        }

        @Override
        public void mouseExited(final MouseEvent e) {
            final Component src = e.getComponent();
            if (src != null) {
                src.setForeground(fgOnNotEntered);
                src.repaint();
            }
        }
    }
    
    public static JButton createMainButton(final String text,
                                           final MouseAdapter adapter,
                                           final Border border,
                                           final Color background,
                                           final Color foreground,
                                           final ActionListener aL) {
        final JButton button = (text == null)? new JButton(): new JButton(text);
        if (adapter != null) {
            button.addMouseListener(adapter);
            button.addMouseMotionListener(adapter);
            button.addMouseWheelListener(adapter);
        }
        if (border != null)
            button.setBorder(border);
        if (background != null) {
            button.setContentAreaFilled(false);
            button.setOpaque(true);
            button.setBackground(background);
        }
        if (foreground != null)
            button.setForeground(foreground);
        if (aL != null)
            button.addActionListener(aL);
        button.setFocusPainted(false);
        return button;
    }
    
    public static JPanel createBoxLayoutPanel(final int axis,
                                              final Component... components) {
        final JPanel panel = new JPanel();
        panel.setLayout(new BoxLayout(panel, axis));
        if (components != null)
            for (final Component c: components) {
                if (!c.isMinimumSizeSet())
                    c.setMinimumSize(c.getPreferredSize());
                if (!c.isMaximumSizeSet())
                    c.setMaximumSize(new Dimension(Short.MAX_VALUE, Short.MAX_VALUE));
                panel.add(c);
            }
        return panel;
    }
    
    public static JLabel label(final String text,
                               final Component labelFor,
                               final Color foreground) {
        final JLabel jLabel = new JLabel(text, JLabel.CENTER);
        jLabel.setForeground(foreground);
        jLabel.setLabelFor(labelFor);
        return jLabel;
    }
    
    public static Box.Filler createNonOpaqueVerticalGlue() {
        final Box.Filler vglue = new Box.Filler(new Dimension(0,0), new Dimension(0,0), new Dimension(0, Short.MAX_VALUE));
        vglue.setOpaque(false);
        return vglue;
    }
    
    public static void main(final String[] args) {
        SwingUtilities.invokeLater(() -> {
            
            final int buttonBorderThickness = 10,
                      paddingThickness = 20;
            final Color borderOnPressed = new Color(182, 25, 25),
                        borderOnNotPressed = new Color(255, 107, 107),
                        backgroundOnPressed = new Color(255, 107, 107),
                        backgroundOnNotPressed = new Color(255, 107, 107),
                        foregroundOnEntered = new Color(253, 210, 199),
                        foregroundOnNotEntered = new Color(1, 36, 67),
                        leftBackground = Color.BLACK,
                        rightBackground = Color.GREEN,
                        labelForeground = Color.LIGHT_GRAY;
            final String firstButtonText = "Main button with long text as label",
                         secondButtonText = "Short",
                         labelText = "Some random text.";
            
            final Border onPressed = BorderFactory.createLineBorder(borderOnPressed, buttonBorderThickness),
                         onNotPressed = BorderFactory.createLineBorder(borderOnNotPressed, buttonBorderThickness),
                         padding = BorderFactory.createEmptyBorder(paddingThickness, paddingThickness, paddingThickness, paddingThickness);
            final MainButtonAdapter mainButtonAdapter = new MainButtonAdapter(foregroundOnEntered, foregroundOnNotEntered, backgroundOnPressed, backgroundOnNotPressed, onPressed, onNotPressed);
            
            final JButton firstButton = createMainButton(firstButtonText, mainButtonAdapter, onNotPressed, backgroundOnNotPressed, foregroundOnNotEntered, null),
                          secondButton = createMainButton(secondButtonText, mainButtonAdapter, onNotPressed, backgroundOnNotPressed, foregroundOnNotEntered, null);
            
            final Dimension secondButtonPreferredSize = secondButton.getPreferredSize();
            secondButton.setMaximumSize(new Dimension(Short.MAX_VALUE, secondButtonPreferredSize.height));
            
            final JLabel lbl = label(labelText, firstButton, labelForeground);
            lbl.setMaximumSize(lbl.getPreferredSize());
            
            final JPanel firstPanel = createBoxLayoutPanel(BoxLayout.X_AXIS, lbl, Box.createHorizontalStrut(paddingThickness), firstButton);
            firstPanel.setOpaque(false);
            firstPanel.setMaximumSize(new Dimension(Short.MAX_VALUE, firstPanel.getPreferredSize().height));
            
            //Ucomment the following lines to get bigger size proportion for the right (green) panel...
            //secondButtonPreferredSize.width = Math.max(500, secondButtonPreferredSize.width);
            //secondButton.setMinimumSize(secondButtonPreferredSize);
            //secondButton.setPreferredSize(secondButtonPreferredSize);
            
            final JPanel left = createBoxLayoutPanel(BoxLayout.Y_AXIS, firstPanel, createNonOpaqueVerticalGlue() /*replace this "glue" with your components...*/);
            left.setBorder(padding);
            left.setBackground(leftBackground);
            
            final JPanel right = createBoxLayoutPanel(BoxLayout.Y_AXIS, secondButton, createNonOpaqueVerticalGlue() /*replace this "glue" with your components...*/);
            right.setBorder(padding);
            right.setOpaque(false); //Could almost be 'right.setBackground(rightBackground);' instead.
            
            final JPanel contents = createBoxLayoutPanel(BoxLayout.X_AXIS, left, right);
            contents.setBackground(rightBackground); //That's why we called 'right.setOpaque(false);' previously.
            
            final JFrame frame = new JFrame("BoxLayout test");
            frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
            frame.getContentPane().add(contents);
            frame.pack();
            //frame.setSize(Math.max(1600, frame.getWidth()), Math.max(900, frame.getHeight()));
            frame.setLocationRelativeTo(null);
            frame.setVisible(true);
        });
    }
}

基本上,内容窗格包含 X_AXIS 上的 BoxLayout 中的两个彩色面板。然后左侧面板包含另一个带有标签和第一个按钮的 BoxLayout 面板。然后就是处理每个 Component.

的最小、首选和最大尺寸

如果你想避免嵌套面板,我想你可以在自定义绘制的面板上使用 GridBagLayout(绘制不同的背景颜色),这也会给你更多的权力来微调使用权重因子调整行为。不过我还没试过。

当你 运行 它时,记得用鼠标调整框架的大小以查看它的行为。

稍后您可以将两个 createNonOpaqueVerticalGlue 方法调用替换为您想要的 Components,因为我假设您稍后会需要黑色和绿色面板中的更多内容。如果您随后需要一些垂直间隙,请使用 vertical strut.