20

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

Let me explain to you with some edited photos:

1. Full Screen GUI

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.

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

FullScreen GUI

2. Small horizontal resize

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

Resized once GUI

3. Minimum horizontal size

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

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.

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

Radiodef
  • 37,180
  • 14
  • 90
  • 125
Kapcash
  • 6,377
  • 2
  • 18
  • 40
  • 1
    Great explanation of the behavior you are looking for. You could hack something together with a `ComponentListener` but it's probably not a good way to do it. Hopefully somebody with more experience than myself sees this and knows of a better way. – Radiodef May 12 '15 at 19:00

5 Answers5

12

Setting the max/min/preferred size of the Green panel can keep that panel the same size under the first condition. 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.

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). To get the true L&F and that you desire you may have to play with the layout/sizes.

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

            }

        });
    }
}
copeg
  • 8,290
  • 19
  • 28
  • Thanks a lot for this, even if I find very strange to put the cyan panel into the green Class panel, I will use it to do exactly what I want ! – Kapcash May 14 '15 at 13:50
4

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.

Forseth11
  • 1,418
  • 1
  • 12
  • 21
  • 1
    `I am not sure this is the proper way:` - don't use null layouts. Swing was designed to be used with layout managers. – camickr May 13 '15 at 05:05
3

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

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.

Durandal
  • 19,919
  • 4
  • 36
  • 70
3

You can use a BoxLayout to contain the green and cyan panel. A BoxLayout respects 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:

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.

camickr
  • 321,443
  • 19
  • 166
  • 288
  • Thanks a lot for this, this way is useful too ! – Kapcash May 14 '15 at 17:17
  • @Kapcash, not sure why you don't like this approach. It is a pure layout approach so you don't write any custom layout code. The solution you accepted basically reinvents the wheel by trying to invoke custom layout code by using a ComponentListener. Not only that you need to create and manage Boolean varaibles, to the layout code is imbedded in the entire class. – camickr May 14 '15 at 18:24
  • The other way allows me to define everthing I need by myself, not only for the actual problem I had. Be sure I will use your solution too, but I was waiting for something like the componentListener which can do a lot more things than a single BoxLayout I already know. Don't be disapointed, I thank you for this as much as 'Copeg'. – Kapcash May 14 '15 at 19:30
  • 2
    `The other way allows me to define everthing I need by myself` - you miss the point of using layout managers. You should NOT be doing everything by yourself. A layout manager keeps all the code self contained in the layout class. Good luck. – camickr May 14 '15 at 20:56
1

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.
  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.

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.

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.

michbau
  • 21
  • 1
  • Thanks, but my questions was solved months ago. And it didn't concern the refreshing sequence of a component but only the controlabillity. – Kapcash Nov 22 '16 at 13:45