Click here to Skip to main content
65,938 articles
CodeProject is changing. Read more.
Articles / Languages / Java

FreeLayout: A New Java Layout

4.88/5 (9 votes)
18 Dec 2013CPOL5 min read 19.5K   176  
This article presents a new flexible Java layout.

Introduction

When it is necessary to create a GUI, one of the big headaches is to arrange the several components (buttons, text fields, lists, ...) into the window. The problem turns bigger if the window must be resized and all the components must be repositioned. And more complicated if in the middle of the process you must create another component and place it in the middle of your window. It is not a complicated task but gives a lot of work.

Java has a list of some layouts that help to create your GUIs but this task can be tricky if you are not familiar with them or the layout that you pretend to use is too complex. You can use a simple GUI tool where you drag and drop the components into the window and design the layout that you wish. But I do not like to use those tools (I feel more comfortable writing each line of code).

So, in this article, I propose a class that deals with this problem without the programmer doing anything (almost nothing). I have called it FreeLayout.

Using the code

FreeLayout is composed of only two methods and a constructor.

There are three constructors available and all of them must receive the dimension of the container (or the container itself) to be able to position the components into the container.

Java
public FreeLayout(Dimension size)
public FreeLayout(int width, int height)
public FreeLayout(Container container)

Next, let's look at the method that is responsible for positioning the components into the container:

Java
public void relativeTo(Component component,
		Anchor leftAnchor,
		Anchor topAnchor,
		Anchor rightAnchor,
		Anchor bottomAnchor)

FreeLayout

FreeLayout assumes that the component position to be added is defined by four Anchors and each Anchor is defined by a component and a gap.

The component can take two possible values: a valid component object (by valid, I mean that the component has already been added using the relativeTo method and in this case, the Anchor refers to a component, like a button, text editor, ...), or a null object (in this case, the Anchor refers to the border of the container).

The gap is an integer number that can be a positive value or negative one, and the fact being a positive or negative influences the position of the component to be added into the container. Let's assume that the component of the Anchor is non null. In this case, whatever the value of gap is, positive or not, will always correspond to the space that separates the component of the Anchor and the component to be added. On the other hand, if the component of the Anchor is null and the gap is a positive value, then this value refers to the space that separates the border and the component to be added. If the gap is a negative value (and the component remains null), this value refers to a dimension of the component to be added (for the top and bottom anchors, this gap refers to the height of the component, for the left and right anchors, the gap refers to the width of the component).

The following table summarizes what has been explained:

Component
non null null
Gap positive gap between
components
gap between component
and container border
negative gap between
components
component width/height

So, let's see an example:

Java
freeLayout.relativeTo(btn3,
		new Anchor(null, 5),
		new Anchor(btn1, 5),
		new Anchor(null, 5),
		new Anchor(null, -20));

In this example, I am adding a component, btn3, to the container, in the following disposition:

  • new Anchor(null, 5): the left side of the btn3 component will be referenced to the left border of the container and the gap between them will be of 5
  • new Anchor(btn1, 5): the top side of the btn3 component will be referenced to the bottom side of btn1 and the gap between them will be of 5
  • new Anchor(null, 5): the right side of the btn3 component will be referenced to the right side of the container and the gap between them will be of 5. This makes the button width go from one end to the other of the container
  • new Anchor(null, -20): the bottom side of the btn3 component is not referenced to any surrounding component because the component is a null object and the gap is a negative value. In this case, I am define that my btn3 component will have a height of 20

In the next table, we can see all possible combinations that a component can receive:

Left Component/
Top Component
Left Gap/
Top Gap
Right Component/
Bottom Component
Right Gap/
Bottom Gap
Comments
null negative null negative Invalid
null negative null positive Width/Height - Container border
null negative non null negative Width/Height - Component
null negative non null positive Width/Height - Component
null positive null negative Container border - Width/Height
null positive null positive Container border - Container border
null positive non null negative Container border - Component
null positive non null positive Container border - Component
non null negative null negative Component - Width/Height
non null negative null positive Component - Container border
non null negative non null negative Component - Component
non null negative non null positive Component - Component
non null positive null negative Component - Width/Height
non null positive null positive Component - Container border
non null positive non null negative Component - Component
non null positive non null positive Component - Component

So far the components are in the right position that we desire but if the window is resizable, the components must be adapted to the new size of the container. This is done by simply calling the invalidate method in the callback method that is called when the container is resized (this method only updates the components that were added using the relativeTo method):

Java
public void invalidate(int width, int height)
public void invalidate(Dimension size)
public void invalidate(Container container)

This method just accepts the new size of the container (or the container itself, as in the constructor) and FreeLayout will position all components keeping the same parameterization defined previously.

Example

Using this layout is very simple as you will see. I will create a simple window to exemplify how to use the FreeLayout class.

There are two things that are essential to making this layout work: set the frame layout to null and give a size to the pane container. I will also implement a component listener to grab the resize event.

Here is the sample code:

Java
import java.awt.Dimension;
import java.awt.GridLayout;
import java.awt.event.ComponentEvent;
import java.awt.event.ComponentListener;
import javax.swing.*;
import freelayout.*;

public class Main extends JFrame implements ComponentListener {

    // Initialize the FreeLayout
    FreeLayout freeLayout = new FreeLayout(300, 300);
    
    public Main() {
        super("FreeLayout Demo");
        setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
        
        // Set the frame layoutof to null and set the container size
        setLayout(null);
        getContentPane().setPreferredSize(new Dimension(300, 300));
        pack();
        
        JButton open = new JButton("open");
        freeLayout.relativeTo(open,
                new Anchor(null, -80),
                new Anchor(null, 5),
                new Anchor(null, 5),
                new Anchor(null, -20));
        getContentPane().add(open);
        
        JTextField textField1 = new JTextField("Text field 1");
        freeLayout.relativeTo(textField1,
                new Anchor(null, 5),
                new Anchor(null, 5),
                new Anchor(open, 5),
                new Anchor(null, -20));
        getContentPane().add(textField1);
        
        JLabel label = new JLabel("Label: ");
        freeLayout.relativeTo(label,
                new Anchor(null, 5),
                new Anchor(textField1, 5),
                new Anchor(null, -40),
                new Anchor(null, -20));
        getContentPane().add(label);
        
        JButton cancel = new JButton("cancel");
        freeLayout.relativeTo(cancel,
                new Anchor(null, -80),
                new Anchor(null, -20),
                new Anchor(null, 5),
                new Anchor(null, 5));
        getContentPane().add(cancel);
        
        JButton ok = new JButton("ok");
        freeLayout.relativeTo(ok,
                new Anchor(null, -80),
                new Anchor(null, -20),
                new Anchor(cancel, 5),
                new Anchor(null, 5));
        getContentPane().add(ok);
        
        JPanel panel = new JPanel();
        panel.setLayout(new GridLayout(3,3));
        panel.add(new JButton("1"));
        panel.add(new JButton("2"));
        panel.add(new JButton("3"));
        panel.add(new JButton("4"));
        panel.add(new JButton("5"));
        panel.add(new JButton("6"));
        panel.add(new JButton("7"));
        panel.add(new JButton("8"));
        panel.add(new JButton("9"));
        freeLayout.relativeTo(panel,
                new Anchor(null, 5),
                new Anchor(null, -150),
                new Anchor(null, 5),
                new Anchor(cancel, 5));
        getContentPane().add(panel);
        
        JTextArea textArea = new JTextArea("Texta area: ");
        freeLayout.relativeTo(textArea,
                new Anchor(label, 5),
                new Anchor(textField1, 5),
                new Anchor(null, 5),
                new Anchor(panel, 5));
        getContentPane().add(textArea);
        
        // To resize the window
        addComponentListener(this);
    }
    
    public static void main(String[] args) {
        new Main().setVisible(true);
    }
    
    @Override
    public void componentHidden(ComponentEvent e) {
    }

    @Override
    public void componentMoved(ComponentEvent e) {
    }

    @Override
    public void componentResized(ComponentEvent e) {
        freeLayout.invalidate(((JFrame) (e.getComponent())).getContentPane().getSize());
    }

    @Override
    public void componentShown(ComponentEvent e) {
    }
}

The result of the code is shown in the next figure:

FreeLayout

Conclusions

As you can see, to create a window using FreeLayout is very simple, and repositioning all components when the window is resized is not a problem. FreeLayout handles everything for you.

Any comments will be welcome!

History

  • 17/12/2013 - Original article.

License

This article, along with any associated source code and files, is licensed under The Code Project Open License (CPOL)