简体   繁体   English

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

[英]How to dynamically control auto-resize components in Java Swing

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.全屏GUI

This is my full screen GUI, with 3 panels and a JToolBar . 这是我的全屏GUI,带有3个面板和一个JToolBar The green one needs to have a fixed size , the others would be resizable . 绿色的需要固定大小 ,其他的则需要调整大小

Currently, I only have 2 small GridLayout to organize them (one vertical, and one horizontal for green and cyan panels). 目前,我只有2个小的GridLayout来组织它们(一个垂直,绿色和青色面板一个水平)。

全屏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 resized according to the new frame size. 例如,如果我从右侧减小框架尺寸,我希望根据新的框架尺寸调整我的蓝色和青色面板的尺寸。 But the green panel must be fixed . 但是绿色面板必须固定 (Not the most difficult part I think.) (这不是我认为最困难的部分。)

一次调整大小的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. 当然,将框架向右拉将使绿色面板再次出现。

GUI的最小大小

How could I do it? 我该怎么办?

I thought of using JSplitPane or 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”软件

Setting the max/min/preferred size of the Green panel can keep that panel the same size under the first condition. 设置Green面板的最大/最小/首选尺寸可以使面板在第一种情况下保持相同的尺寸。 To check for resizes, you can use a ComponentListener on one of the other JPanel 's - if the size gets below a particular width then change the max/min/preferred size of the Green panel. 要检查大小,可以在其他JPanel之一上使用ComponentListener如果大小小于特定宽度,则更改“ Green面板的最大/最小/首选大小。

Below is a hacked together example of doing this - it resizes the Green panel when the Blue is < 600, and the resize is a weighted resize (30% of total width). 以下是一起执行此操作的示例-当Blue <600时,它将调整Green面板的大小,并且调整大小是加权调整大小(占总宽度的30%)。 To get the true L&F and that you desire you may have to play with the layout/sizes. 要获得真正的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);

            }

        });
    }
}

This is how I do it and I am not sure this is the proper way: 这是我的操作方式,我不确定这是否正确:

First set the layout to null. 首先将布局设置为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. 我为一些程序执行了此操作,以使框架左右两侧的滚动窗格始终保持相同的宽度,并使其他所有内容的大小都随着调整大小而变大。

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,它们将随容器调整大小。

You can use a BoxLayout to contain the green and cyan panel. 您可以使用BoxLayout包含绿色和青色面板。 A BoxLayout respects the minimum and maximum sizes of a component. BoxLayout遵循组件的最小和最大大小。 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: 因此,对于绿色面板,将最大大小设置为等于首选大小,对于青色面板,将最小大小设置为等于首选大小:

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()方法,以仅返回面板的首选大小。

The important sequence is this: 重要的顺序是这样的:

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

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.GridBagLayout in combination with the java.awt.GridBagConstraints and 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. 如果将一个容器添加到另一个容器中,请在每个容器上使用一个布局,否则由于层次结构中缺少布局,计算将失败。

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM