bekwam courses

Great Grid Shootout - FX and Swing

September 3, 2017

This series of articles will compare and contrast flexible grid implementations in various UI toolkits. This first post is on grids in the Java desktop ecosystem, JavaFX and Swing.

Specification

The reference application will use a 3x4 grid containing components that span rows and span columns. The grid will contain 7 buttons. The default dimensions of the toolkit's button will be used as the basic unit of the grid. For buttons spanning columns and rows, the dimensions will be adjusted to fit the available space. This screenshot is a sketch of the specification.

Sketch of a Grid
Grid with Some Buttons Spanning Columns and Rows

JavaFX

JavaFX provides a special-purpose container called GridPane for managing a flexible grid. As with all JavaFX containers, components are added to GridPane using the add() method. For GridPane, there are extra arguments for spanning information. For example,

	gridPane.add( mynode, 0, 1, 2, 3 );	

adds the component "mynode" to the gridPane container to the second row and the first column. "mynode" will be given 2 units of width and 3 units of height. This is a convenient function that avoids working with gridPane's children and constraints objects. This screenshot shows an implementation of the reference app.

JavaFX Showing a GridPane
JavaFX Implementation

The code for the above implementation follows. Notice the setMaxSize() on the Button objects. This is to break the max width and height restriction so that "gp" is able to grow the Button to fit the GridPane cell.

public class GreatGridShootoutApp extends Application {

    @Override
    public void start(Stage primaryStage) throws Exception {

        Button[] buttons = new Button[7];

        for( int i=0; i<buttons.length; i++ ) {
            buttons[i] = new Button("Button " + (i+1) );
            buttons[i].setMaxSize(Double.MAX_VALUE, Double.MAX_VALUE );
        }

        GridPane gp = new GridPane();

        gp.add( buttons[0], 0, 0 );
        gp.add( buttons[1], 0, 1 );
        gp.add( buttons[2], 1, 0, 2, 2 );
        gp.add( buttons[3], 0, 2, 3, 1 );
        gp.add( buttons[4], 0, 3 );
        gp.add( buttons[5], 1, 3 );
        gp.add( buttons[6], 2, 3 );

        Scene scene = new Scene( gp );

        primaryStage.setScene( scene );
        primaryStage.show();
    }

    public static void main(String[] args) {
        launch(args);
    }
}

Swing

In Swing, you need to manipulate a GridBagConstraints object to provide the location and spanning information. This makes the code a little bulkier as you need to make several setter calls on the object before adding it to the container. This screenshot shows an implementation in Swing.

Swing Showing a GridPane
Swing Implementation

Four setters are needed to set up the GridBagConstraints object gbc. You can reduce this by using the old RELATIVE and REMAINDER constants, but this seems more confusing to me than specifying an (x,y) position. The Swing JButtons will resize automatically based on a gbc settings of fill=BOTH.

public class GreatGridShootoutSwingApp extends JFrame {

    public GreatGridShootoutSwingApp(String title) throws HeadlessException {
        super(title);

        JButton[] buttons = new JButton[7];
        for( int i=0; i<buttons.length; i++ ) {
            buttons[i] = new JButton("Button " + (i+1));
        }

        Container contents = getContentPane();
        contents.setLayout( new GridBagLayout() );

        GridBagConstraints gbc = new GridBagConstraints();
        gbc.fill = BOTH;

        gbc.gridx = 0; gbc.gridy = 0;
        gbc.gridwidth = 1; gbc.gridheight = 1;
        contents.add( buttons[0], gbc );

        gbc.gridx = 0; gbc.gridy = 1;
        gbc.gridheight = 1; gbc.gridwidth = 1;
        contents.add( buttons[1], gbc );

        gbc.gridx = 1; gbc.gridy = 0;
        gbc.gridwidth = 2; gbc.gridheight = 2;
        contents.add( buttons[2], gbc );

        gbc.gridx = 0; gbc.gridy = 2;
        gbc.gridwidth = 3; gbc.gridheight = 1;
        contents.add( buttons[3], gbc );

        gbc.gridx = 0; gbc.gridy = 4;
        gbc.gridwidth = 1; gbc.gridheight = 1;
        contents.add( buttons[4], gbc );

        gbc.gridx = 1; gbc.gridy = 4;
        contents.add( buttons[5], gbc );

        gbc.gridx = 2; gbc.gridy = 4;
        contents.add( buttons[6], gbc );
    }

    public static void main(String[] args) {

        JFrame app = new GreatGridShootoutSwingApp("Grid Shootout Swing" );

        app.setDefaultCloseOperation(DISPOSE_ON_CLOSE);
        app.addWindowListener(new WindowAdapter() {
            @Override
            public void windowClosed(WindowEvent e) {
                System.exit(0);
            }
        });

        app.pack();
        app.setVisible(true);
    }
}
	

The JavaFX add() is a convenient method that wraps up access to the GridPane's children and constraints objects. That makes the code cleaner because a single add() will contain all of the positioning (x,y) and size (length, width) information. If you're working with Swing, you'll need to modify the supporting objects directly and pass around an object in the add() call. It's worthwhile to create a utility method or subclass to mimic JavaFX's add().

If you have any questions or found an error, feel free to send email to webmaster@bekwam.com.


Headshot of Carl Walker

By Carl Walker

President and Principal Consultant of Bekwam, Inc