java - 为什么即使在设置 JPanel 的大小后,我的框布局也会更改大小

标签 java swing size layout-manager boxlayout

我一直在尝试调整我的 JPanel 大小,同时使用 LayoutManager(例如 BoxLayout)以特定顺序排列该布局内的组件,我通过不使用 Box 实现了我想要的外观布局管理器。

我想要在这张图片中的原始想法:

enter image description here

但我想在使用面板的框布局管理器时实现这一点,当我这样做时,我得到以下输出:

添加 BoxLayout 时得到的输出:

enter image description here

这是代码:

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) {

}
}

菜单按钮.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的源代码,我意识到它根据鼠标事件绘制边框和背景。您不必自己执行此操作,因为 JButton 支持 Border 和背景颜色。因此,您可以创建一个 MouseListener 来更新这些属性并让它用这些属性重新绘制自身(我将在下面的代码示例中进行演示)。

无论如何,回到你的主要问题,这里是你可以如何做到这一点,只使用 BoxLayout :

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

基本上,内容 Pane 包含 X_AXISBoxLayout 中的两个彩色面板。然后左侧面板包含另一个带有标签和第一个按钮的 BoxLayout 面板。然后就是处理每个组件的最小、首选和最大尺寸。

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

运行时,请记住用鼠标调整框架大小以查看其行为。

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

关于java - 为什么即使在设置 JPanel 的大小后,我的框布局也会更改大小,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/68949746/

相关文章:

c++ - 二维 vector 的大小

java - DEFAULT_SIZE 和 PREFERRED_SIZE 有什么区别?

java - 没有代理就无法解决 Maven 依赖关系

java - 将jquery折线图绘制到数据库值

java - 如何让 SWT 中的 TreeViewer 正确刷新?

java - 由于图像加载,GUI 不断崩溃?

java - JPanel 何时绘制(或重新绘制)其子组件?

java - 在Java中获取数字异常的倒数

java - 如何强制设置 Java (.jar) 应用程序窗口的大小?

java - 如何关联不同 ArrayList 中的两个元素?