如何在Java Swing中动态控制自动调整大小的组件

20

在Java(1.8)Swing中创建新的GUI,我正在寻找一种方法来覆盖所有组件的调整大小行为。

让我用一些编辑过的照片向您解释:

1. 全屏GUI

这是我的全屏GUI,有3个面板和一个JToolBar。 绿色面板需要具有固定大小,其他面板应该是可调整大小的

目前,我只有两个小的GridLayout来组织它们(一个垂直的和一个水平的用于绿色和青色面板)。

FullScreen GUI

2. 小型水平调整大小

例如,如果我从右侧减小窗口大小,我希望我的蓝色和青色面板根据新的窗口大小进行调整大小。但是绿色面板必须是固定的。(我认为这不是最困难的部分)

Resized once GUI

3. 水平最小尺寸

这对我来说是最困难的部分。一旦青色(或蓝色)面板达到其最小尺寸,我希望它将绿色面板""向左侧,即使它会从窗口中消失。

当然,将窗口向右拉动将使绿色面板重新出现。

Minimum size of the GUI

我该怎么做?

我考虑使用JSplitPane或特定的监听器来手动决定调整大小的行为,但我需要一些建议。

如果有现成的帖子可以回答这个问题,请原谅,我没有找到任何解释我的问题的答案。

预先感谢您!


如果您想要更多示例,请查看表现相同的"Evernote"软件


1
你对所需的行为进行了很好的解释。你可以使用ComponentListener来拼凑一些东西,但这可能不是一个好方法。希望有比我更有经验的人看到这个问题,并知道更好的方法。 - Radiodef
5个回答

12

设置Green面板的最大/最小/首选大小可以在第一个条件下保持该面板的相同大小。为了检查大小调整,您可以在其他JPanel上使用ComponentListener - 如果大小小于特定宽度,则更改Green面板的最大/最小/首选大小。

以下是一个简单的示例 - 当Blue小于600时,它会重新调整Green面板的大小,并进行加权调整(总宽度的30%)。要获取真正的外观和所需的样式,您可能需要调整布局/大小。

import java.awt.BorderLayout;
import java.awt.Color;
import java.awt.Dimension;
import java.awt.GridBagConstraints;
import java.awt.GridBagLayout;
import java.awt.GridLayout;
import java.awt.Insets;
import java.awt.Rectangle;
import java.awt.event.ComponentAdapter;
import java.awt.event.ComponentEvent;
import java.awt.event.ComponentListener;

import javax.swing.Box;
import javax.swing.JFrame;
import javax.swing.JPanel;
import javax.swing.JToolBar;
import javax.swing.SwingUtilities;


public class GridTest extends JPanel{

    private boolean changeAllowed = false;
    //keep reference to cyan for the height dimension
    final JPanel cyan = new JPanel();

    public GridTest(){
        cyan.setPreferredSize(new Dimension(600, 300));//provide sizing hint
    }

    private Dimension getCustomDimensions(){
        if ( changeAllowed ){
            return new Dimension((int)(super.getParent().getBounds().width * 0.3), cyan.getBounds().height);
        }else{
            return new Dimension(200, cyan.getBounds().height);
        }
    }
    @Override
    public Dimension getMaximumSize(){
        return getCustomDimensions();
    }
    @Override
    public Dimension getMinimumSize(){
        return getCustomDimensions();
    }
    @Override
    public Dimension getPreferredSize(){
        return getCustomDimensions();
    }

    public static void main(String[] args) throws Exception{
        SwingUtilities.invokeAndWait(new Runnable(){

            @Override
            public void run() {
                final int MINIMUM = 600;
                JFrame frame = new JFrame();
                frame.add(new JToolBar(), BorderLayout.NORTH);
                final JPanel blue = new JPanel();

                final GridTest green = new GridTest();
                green.setBackground(Color.green);
                green.setOpaque(true);

                green.cyan.setBackground(Color.cyan);
                green.cyan.setOpaque(true);
                blue.setOpaque(true);
                blue.setBackground(Color.blue);
                blue.setPreferredSize(new Dimension(900, 300));//hint at size
                blue.setMinimumSize(new Dimension(100, 200));//hint at size
                //Nest Box Layouts
                Box top = Box.createHorizontalBox();
                top.add(blue);
                Box bottom = Box.createHorizontalBox();
                bottom.add(green);
                bottom.add(green.cyan);
                Box vert = Box.createVerticalBox();
                vert.add(top);
                vert.add(bottom);
                frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
                frame.add(vert);
                //listen for resizes
                blue.addComponentListener(new ComponentAdapter(){

                    @Override
                    public void componentResized(ComponentEvent e) {
                        if ( blue.getBounds().width < MINIMUM ){//set flag
                            green.changeAllowed = true;
                        }else{
                            green.changeAllowed = false;
                        }
                    }
                });

                frame.pack();
                frame.setSize(800, 600);
                frame.setVisible(true);

            }

        });
    }
}

非常感谢您,尽管我觉得将青色面板放入绿色类面板中非常奇怪,但我会用它来做我想做的事情! - Kapcash

4

这是我做的方式,但我不确定这是否是正确的方法:

首先将布局设置为null。

接下来为您的窗口创建一个组件调整大小事件:

addComponentListener(new ComponentAdapter(){
            public void componentResized(ComponentEvent e){
            }
        });

在这里,您可以手动更改组件的大小。在我的一些程序中,我已经这样做了,以保持框架左右两侧的滚动窗格始终具有相同的宽度,并使其他所有内容随着调整大小而上下变化。


1
我不确定这是否是正确的方法:不要使用空布局。Swing被设计为与布局管理器一起使用。 - camickr

3

如果你为绿色和青色组件设置适当的最小尺寸,那么使用GridBagLayout应该很容易:

setLayout(new GridBagLayout());
green.setMnimumSize(new Dimension(0, 0));
cyan.setMinimumSize(new Dimension(100, 100)); // whatever fits
add(blue, new GridBagConstraints(0, 0, 2, 1,
        1.0, 0.0,
        GridBagConstraints.WEST, GridBagConstraints.HORIZONTAL,
        new Insets(0, 0, 0, 0), 0, 0);
add(green, new GridBagConstraints(0, 1, 1, 1,
        0.0, 0.0,
        GridBagConstraints.WEST, GridBagConstraints.NONE,
        new Insets(0, 0, 0, 0), 0, 0);
add(cyan, new GridBagConstraints(1, 1, 1, 1,
        1.0, 0.0,
        GridBagConstraints.WEST, GridBagConstraints.HORIZONTAL,
        new Insets(0, 0, 0, 0), 0, 0);

通过为青色和蓝色组件指定权重x,并将resize = HORIZONTAL设置为容器大小,它们将随容器一起调整大小。

3
您可以使用BoxLayout来包含绿色和青色面板。 BoxLayout尊重组件的最小和最大尺寸。 因此,对于绿色面板,您将最大尺寸设置为首选尺寸,对于青色面板,您将最小尺寸设置为首选尺寸:
import java.awt.*;
import java.util.*;
import javax.swing.*;

public class SSCCE extends JPanel
{
    public SSCCE()
    {
        JPanel blue = new JPanel();
        blue.setBackground( Color.BLUE );
        blue.setPreferredSize( new Dimension(500, 100) );

        Box box = Box.createHorizontalBox();

        JPanel green = new JPanel();
        green.setBackground( Color.GREEN );
        green.setPreferredSize( new Dimension(200, 100) );
        green.setMaximumSize( green.getPreferredSize() );
        box.add( green );

        JPanel cyan = new JPanel();
        cyan.setBackground( Color.CYAN );
        cyan.setPreferredSize( new Dimension(300, 100) );
        cyan.setMinimumSize( cyan.getPreferredSize() );
        box.add( cyan );

        setLayout( new BorderLayout() );
        add(blue, BorderLayout.NORTH);
        add(box, BorderLayout.CENTER);
    }

    private static void createAndShowGUI()
    {
        JFrame frame = new JFrame("SSCCE");
        frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
        frame.add(new SSCCE());
        frame.setLocationByPlatform( true );
        frame.pack();
        frame.setVisible( true );
    }

    public static void main(String[] args)
    {
        EventQueue.invokeLater(new Runnable()
        {
            public void run()
            {
                createAndShowGUI();
            }
        });
    }
}

当然,正确的解决方案是分别覆盖每个面板的getMaximumSize()getMinimumSize()方法,使其返回面板的首选大小。

非常感谢您,这种方式也很有用! - Kapcash
@Kapcash,不确定为什么您不喜欢这种方法。它是一种纯布局方法,因此您不需要编写任何自定义布局代码。您接受的解决方案基本上通过使用ComponentListener尝试调用自定义布局代码来重新发明轮子。不仅如此,您还需要创建和管理布尔变量,以便布局代码嵌入整个类中。 - camickr
另一种方式允许我自己定义我需要的一切,不仅仅是针对我遇到的实际问题。请放心,我也会使用您的解决方案,但我正在等待像componentListener这样可以做更多事情的组件,而不仅仅是我已经知道的单个BoxLayout。 不要失望,我和'Copeg'一样感谢您。 - Kapcash
2
“另一种方法允许我自己定义所需的一切。” - 你没有理解使用布局管理器的重点。你不应该全部由自己完成。布局管理器将所有代码都包含在布局类中,使其自包含。祝好运。 - camickr

1
重要的顺序是这样的:


                frame.revalidate();
                frame.pack();
                frame.repaint();
  1. revalidate():在组件层次结构被验证之后,使无效的组件无效,并从最近的验证根开始验证。
  2. pack():使窗口大小适应其子组件的首选大小和布局。窗口的宽度和高度会自动增加。
  3. repaints():在所有这些重新计算完成之后,重绘显示组件。

在您的代码中进行一系列更改并希望看到结果出现在屏幕上时,请使用此序列。

我喜欢将java.awt.GridBagLayoutjava.awt.GridBagConstraints结合使用,并在从内容窗格开始的所有容器上使用此布局。

如果您将一个容器添加到另一个容器中,请在它们的每个容器上使用布局,否则由于层次结构中缺少布局而导致计算失败。


谢谢,但我的问题几个月前就解决了。它并不涉及组件的刷新顺序,而只是可控性的问题。 - Kapcash

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接