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

声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow 原文地址: http://stackoverflow.com/questions/30198953/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me): StackOverFlow

提示:将鼠标放在中文语句上可以显示对应的英文。显示中英文
时间:2020-08-11 09:18:28  来源:igfitidea点击:

How to dynamically control auto-resize components in Java Swing

javaswinguser-interfaceresizeautoresize

提问by Kapcash

Creating a new GUI in Java (1.8) Swing, I am searching for a way to override resize behavior of all my components.

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

Let me explain to you with some edited photos:

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

1. Full Screen GUI

1. 全屏图形用户界面

This is my full screen GUI, with 3 panels and a JToolBar. The green one needs to have a fixed size, the others would be resizable.

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

Currently, I only have 2 small GridLayoutto organize them (one vertical, and one horizontal for green and cyan panels).

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

FullScreen GUI

全屏图形用户界面

2. Small horizontal resize

2. 小的水平调整大小

If I, for example, reduce the frame size from the right side, I want my blue and cyan panel to be resizedaccording to the new frame size. But the green panel must be fixed. (Not the most difficult part I think.)

例如,如果我从右侧缩小框架尺寸,我希望根据新框架尺寸调整蓝色和青色面板的尺寸。但绿色面板必须固定。(不是我认为最困难的部分。)

Resized once GUI

调整一次 GUI

3. Minimum horizontal size

3. 最小水平尺寸

This is the most difficult part for me. As soon as the cyan (or blue) panel reach is minimum size, I want him to "push" the green panel to the left, even if it disappears off the frame.

这对我来说是最困难的部分。一旦青色(或蓝色)面板达到最小尺寸,我希望他将绿色面板向左“”,即使它从框架上消失。

Of course, pulling the frame to the right will make the green panel appear again.

当然,向右拉动框架会使绿色面板再次出现。

Minimum size of the GUI

GUI 的最小尺寸

How could I do it?

我怎么能做到?

I thought of using JSplitPaneor a specific listener to manually decide the resize behavior but I need some advice.

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

Sorry if an existing post can answer this, I didn't find any answer explaining the issue for me.

抱歉,如果现有帖子可以回答这个问题,我没有找到任何可以为我解释问题的答案。

Thanks in advance!

提前致谢!



If you want more examples, look at the "Evernote" software which acts the same way

如果您需要更多示例,请查看作用相同的“Evernote”软件

采纳答案by copeg

Setting the max/min/preferred size of the Greenpanel can keep that panel the same size under the first condition. To check for resizes, you can use a ComponentListeneron one of the other JPanel's - if the size gets below a particular width then change the max/min/preferred size of the Greenpanel.

设置Green面板的最大/最小/首选尺寸可以使该面板在第一种条件下保持相同的尺寸。要检查调整大小,您可以ComponentListener在另一个JPanel's上使用 a - 如果大小低于特定宽度,则更改Green面板的最大/最小/首选大小。

Below is a hacked together example of doing this - it resizes the Greenpanel when the Blueis < 600, and the resize is a weighted resize (30% of total width). To get the true L&F and that you desire you may have to play with the layout/sizes.

下面是一个 hacked together 示例 - 它在< 600Green时调整面板大小Blue,并且调整大小是加权调整大小(总宽度的 30%)。要获得真正的 L&F 和您想要的效果,您可能需要调整布局/尺寸。

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

            }

        });
    }
}

回答by Forseth11

This is how I do it and I am not sure this is the proper way:

这就是我的做法,我不确定这是正确的方法:

First set the layout to null.

首先将布局设置为空。

Next create a component resized event for your frame:

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

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

Inside here you can manually make changes to the components as it is resized. I have done this for a few of my programs to keep a scroll pane on the left and right of the frame always the same width and have everything else size up and down as resized.

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

回答by Durandal

This should be easy with a GridBagLayout, provided you set appropiate minimum sizes for the green and cyan components:

使用 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);

By specifying a weight x for cyan and blue component as well as setting resize = HORIZONTAL they resize with the container.

通过为青色和蓝色分量指定权重 x 以及设置 resize = HORIZONTAL,它们会随容器一起调整大小。

回答by camickr

You can use a BoxLayoutto contain the green and cyan panel. A BoxLayoutrespects the minimum and maximum sizes of a component. So for the green panel you set then maximum size equal to the preferred size and for the cyan panel you set the minimum size equal to the preferred size:

您可以使用 aBoxLayout来包含绿色和青色面板。ABoxLayout尊重组件的最小和最大尺寸。因此,对于绿色面板,您设置的最大尺寸等于首选尺寸,而对于青色面板,您将最小尺寸设置为首选尺寸:

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

Of course the proper solution is to override the getMaximumSize()and getMinimumSize()methods respectively of each panel to just return the preferred size of the panel.

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

回答by michbau

The important sequence is this:

重要的顺序是这样的:

                frame.revalidate();
                frame.pack();
                frame.repaint();
  1. revalidate(): invalidates the components up to the nearest root afterwards the component hierarchyis validated starting from the nearest validate root.
  2. pack(): Causes the window to be sized to fit the preferred size and layouts of its subcomponents. The resulting width and hight of the window are automatically enlarged..
  3. repaints(): After all this recalculations, the repaint shows the components.
  1. revalidate():在从最近的验证根开始验证组件层次结构之后,使组件失效直到最近的根。
  2. pack():使窗口的大小适合其子组件的首选大小和布局。生成的窗口宽度和高度会自动放大。
  3. repaints():在所有这些重新计算之后,重绘显示组件。

Use this sequence after each series of changes, you have done in your code and want to see the results appearing on screen.

在每一系列更改后使用此序列,您已在代码中完成并希望看到出现在屏幕上的结果。

I like the java.awt.GridBagLayoutin combination with the java.awt.GridBagConstraintsand I use this layout on all containers starting up from the content pane.

我喜欢java.awt.GridBagLayout中与组合java.awt.GridBagConstraints中,我使用从内容窗格中启动所有的容器此布局。

If you add a container into an other container use a layout on each of them, otherwise the calculation fails because of a missing layout in the hierarchy.

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