GridBag Layouts

 

Introduction.  Remember some of the more irritating aspects of working with GridLayouts?  Some of the major things I don't like are
  • You must add components is a specific order without actually clearly specifying the location
  • Things like textfields and buttons automatically stretch to fill a cell
  • You can't really align controls in a cell like labels and textfields
  • You can't place things across multiple cells
  • All the cells have equal width and height

Well with GridBagLayouts you can get around all of these.  Though is does come at the price of some extra effort.  But there are a large variety of extra methods and properties that can be used to modify a particular layout.

But first we present the constructor.

 

Constructor Description
GridBagLayout( ) This constructor is used with no arguments

 

But in order to modify and exert more control over the layout the developer works with an additional class called GridBagConstraints.  The constructor for this follows.

 

Constructor Description
GridBagConstraints( ) This constructor is used with no arguments

 

GridBagConstraints have a pretty large variety of properties and methods to modify a GridBagLayout.

 

Property Description
gridx x position in the grid where x denotes the row
gridy y position in the grid where y denotes the column
gridwidth number of cells combined to put together a cell width
gridheight number of cells combined to put together a cell height
fill some constants used to assign whether the components should fill a cell
  • GridBagConstraints.NONE
    • no fill
  • GridBagConstraints.BOTH
    • fill both height and width
  • GridBagConstraints.HORIZONTAL
    • fills width
  • GridBagConstraints.VERTICAL
    • fills height
anchor some constants that assign the alignment of a component in a cell
  • GridBagConstraints.NORTH
  • GridBagConstraints.NORTHEAST
  • GridBagConstraints.EAST
  • GridBagConstraints.SOUTHEAST
  • GridBagConstraints.SOUTH
  • GridBagConstraints.SOUTHWEST
  • GridBagConstraints.WEST
  • GridBagConstraints.NORTHWEST
  • GridBagConstraints.CENTER
weightx the percentage of the entire row width that should be assigned to a particular cell - inexact but useful
weighty the percentage of the entire column height that should be assigned to a particular cell - inexact but useful

 

One of the things I like to do for developing the constraints is due it in a method called buildConstraints.  I have a copy of the method we are about to use in a subsequent program.  I want to discuss it in more detail before we make use of it.  The method follows.

 

// a method to build the constraints for each cell of a GridBagLayout
public void buildConstraints(GridBagConstraints gbc, int gx, int gy, int fillStatus, int pos, int wx, int wy, int gw)
{

gbc.gridx = gx; // specify x location of cell
gbc.gridy = gy; // specify y location of cell
gbc.fill = fillStatus; // specify whether or not object should fill the cell
gbc.anchor = pos; // specify the object's alignment in the cell
gbc.weightx = wx; // specify the weight associated with the width of the cell
gbc.weighty = wy; // specify the weight associated with the height of the cell
gbc.gridwidth = gw; // specify width of the cell in the number of cells

}

 

This way, whenever we want to change the properties of a control on the GUI we can just pass certain parameters to this method rather than keep repeating these sorts of lines of code within the program.

There are three things you should know about building constraints.

  • you need to build the constraints before you add the control to the panel
  • you need to instantiate and name the GridBagConstraints, which is usually done just after you instantiate the GridBagLayout( )
  • you need to refer to the name of the constraints when you add the control to the GUI

Notice that we can now be much more flexible in developing our GUIs.

An Example.  Now we will finally develop our GridBagLayout GUI.  The program is relatively simple so that we can focus on aspects of the GUI and event listeners.  This is our first program where I have it listening for two different types of events.  You should call the applet BreakEven.java.

 

import java.awt.*;
import java.awt.event.*;
import javax.swing.*;
import javax.swing.event.*;
import java.text.DecimalFormat;

public class BreakEven extends JApplet implements ActionListener
{

// initializing computation variables
private double newProfit, currentProfit, improvementCost, breakEven;
boolean validInputs;

// input radio buttons
JRadioButton[] timeUnits = new JRadioButton[4];
// input labels, textfields
JLabel lblNewProfit = new JLabel("Anticipated Profit: ");
JTextField txtNewProfit = new JTextField(7);
JLabel lblCurrentProfit = new JLabel("Current Profit: ");
JTextField txtCurrentProfit = new JTextField(7);
JLabel lblImprovementCost = new JLabel("The Cost of Improvement: ");
JTextField txtImprovementCost = new JTextField(7);
// input calculate button
JButton cmdCalculate = new JButton("Calculate Break Even");

// labels and textfields for time until break even output
JLabel lblBreakEven = new JLabel("The Time Until Break Even: ");
JTextField txtBreakEven = new JTextField(8);

// textfields for output of time units
JTextField txtNewProfitTimeUnits = new JTextField(8);
JTextField txtCurrentProfitTimeUnits = new JTextField(8);
JTextField txtBreakEvenTimeUnits = new JTextField(8);

public void init( )
{

// instantiate the grid that will coordinate the overall layout
JPanel panOverall = new JPanel();
GridBagLayout gblOverall = new GridBagLayout();
GridBagConstraints gbcOverall = new GridBagConstraints();
panOverall.setLayout(gblOverall);

// develop label and textbox for the cost of improvement input
buildConstraints(gbcOverall, 0, 0, GridBagConstraints.NONE, GridBagConstraints.EAST, 50, 100, 2);
panOverall.add(lblImprovementCost, gbcOverall);
buildConstraints(gbcOverall, 2, 0, GridBagConstraints.NONE, GridBagConstraints.WEST, 50, 100, 2);
panOverall.add(txtImprovementCost, gbcOverall);

// develop the Radio Buttons
timeUnits[0] = new JRadioButton("years");
timeUnits[1] = new JRadioButton("quarters");
timeUnits[2] = new JRadioButton("months");
timeUnits[3] = new JRadioButton("weeks");

// defining the button group for the radio buttons
ButtonGroup bgTimeUnits = new ButtonGroup();

// using a user defined class to handle radio button events
RadioButtonHandler handler = new RadioButtonHandler();
// grouping, configuring and placing the radio buttons into the GUI
for (int i = 0; i < timeUnits.length; i++)
{

bgTimeUnits.add(timeUnits[i]);
timeUnits[i].addItemListener(handler);
buildConstraints(gbcOverall, i, 1, GridBagConstraints.NONE, GridBagConstraints.CENTER, 25, 100, 1);
panOverall.add(timeUnits[i], gbcOverall);

}

// develop label and textbox for current profit input
buildConstraints(gbcOverall, 0, 2, GridBagConstraints.NONE, GridBagConstraints.EAST, 50, 100, 2);
panOverall.add(lblCurrentProfit, gbcOverall);
buildConstraints(gbcOverall, 2, 2, GridBagConstraints.NONE, GridBagConstraints.WEST, 25, 100, 1);
panOverall.add(txtCurrentProfit, gbcOverall);
buildConstraints(gbcOverall, 3, 2, GridBagConstraints.NONE, GridBagConstraints.WEST, 25, 100, 1);
txtCurrentProfitTimeUnits.setEditable(false);
panOverall.add(txtCurrentProfitTimeUnits, gbcOverall);

// develop label and textbox for anticipated profit input
buildConstraints(gbcOverall, 0, 3, GridBagConstraints.NONE, GridBagConstraints.EAST, 50, 100, 2);
panOverall.add(lblNewProfit, gbcOverall);
buildConstraints(gbcOverall, 2, 3, GridBagConstraints.NONE, GridBagConstraints.WEST, 25, 100, 1);
panOverall.add(txtNewProfit, gbcOverall);
buildConstraints(gbcOverall, 3, 3, GridBagConstraints.NONE, GridBagConstraints.WEST, 25, 100, 1);
txtNewProfitTimeUnits.setEditable(false);
panOverall.add(txtNewProfitTimeUnits, gbcOverall);

// build the constraints for the command button
buildConstraints(gbcOverall, 0, 4, GridBagConstraints.NONE, GridBagConstraints.CENTER, 100, 100, 4);
cmdCalculate.addActionListener(this);
panOverall.add(cmdCalculate, gbcOverall);

// develop label and textbox for Break Even outputs
buildConstraints(gbcOverall, 0, 5, GridBagConstraints.NONE, GridBagConstraints.EAST, 50, 100, 2);
panOverall.add(lblBreakEven, gbcOverall);
buildConstraints(gbcOverall, 2, 5, GridBagConstraints.NONE, GridBagConstraints.WEST, 25, 100, 1);
txtBreakEven.setEditable(false);
panOverall.add(txtBreakEven, gbcOverall);
buildConstraints(gbcOverall, 3, 5, GridBagConstraints.NONE, GridBagConstraints.WEST, 25, 100, 1);
txtBreakEvenTimeUnits.setEditable(false);
panOverall.add(txtBreakEvenTimeUnits, gbcOverall);

setContentPane(panOverall);

}

// a method to build the constraints for each cell of a GridBagLayout
public void buildConstraints(GridBagConstraints gbc, int gx, int gy, int fillStatus, int pos, int wx, int wy, int gw)
{

gbc.gridx = gx; // specify x location of cell
gbc.gridy = gy; // specify y location of cell
gbc.fill = fillStatus; // specify whether or not object should fill the cell
gbc.anchor = pos; // specify the object's alignment in the cell
gbc.weightx = wx; // specify the weight associated with the width of the cell
gbc.weighty = wy; // specify the weight associated with the height of the cell
gbc.gridwidth = gw; // specify width of the cell in the number of cells

}


// the actionEvent handler
public void actionPerformed(ActionEvent userClick)
{

if(validateInputs( ))
{

if (userClick.getSource( ) == cmdCalculate)
{

computeBreakEven(improvementCost, currentProfit, newProfit);

}

}

}

public void computeBreakEven( double cost, double currentP, double newP )
{

breakEven = cost/(newP - currentP);
DecimalFormat twoDecimalPlaces = new DecimalFormat("0.00");

txtBreakEven.setText(twoDecimalPlaces.format(breakEven));

}

// a method to obtain inputs from the GUI
// and validate them

public boolean validateInputs( )
{

validInputs = true;
String nonNumericMessage = "";
// obtaining inputs from the GUI
// the try block is used to help insure the
// inputs are numeric of the appropriate types

try
{

improvementCost = Double.parseDouble(txtImprovementCost.getText());

}

catch (NumberFormatException nfeCost)
{

nonNumericMessage = "You need to enter a number for the improvement cost\n";
validInputs = false;
txtImprovementCost.setText("");

}

try
{

currentProfit = Double.parseDouble(txtCurrentProfit.getText());

}
catch (NumberFormatException nfeCurrent)
{

nonNumericMessage = nonNumericMessage + "You need to enter a number for the current profit\n";
validInputs = false;
txtCurrentProfit.setText("");

}

try
{

newProfit = Double.parseDouble(txtNewProfit.getText());

}

catch (NumberFormatException nfeNew)
{

nonNumericMessage = nonNumericMessage + "You need to enter a number for the anticipated profit\n";
validInputs = false;
txtNewProfit.setText("");

}

// this if statement displays an error message associated with non-numeric inputs
if (!validInputs)
JOptionPane.showMessageDialog(null, nonNumericMessage, "Invalid Inputs", JOptionPane.ERROR_MESSAGE);

return validInputs;

} // end method to validate the inputs

private class RadioButtonHandler implements ItemListener
{

public void itemStateChanged( ItemEvent event )
{

String timePeriod = "";
String timeRate = "";

if (event.getSource() == timeUnits[0])
{

timePeriod = "years";
timeRate = "per year";

}
else if (event.getSource() == timeUnits[1])
{

timePeriod = "quarters";
timeRate = "per quarter";

}
else if (event.getSource() == timeUnits[2])
{

timePeriod = "months";
timeRate = "per month";

}
else if (event.getSource() == timeUnits[3])
{

timePeriod = "weeks";
timeRate = "per week";

}

txtCurrentProfitTimeUnits.setText(timeRate);
txtNewProfitTimeUnits.setText(timeRate);
txtBreakEvenTimeUnits.setText(timePeriod);

}

} // ends radio button handler

} // ends the BreakEven class

 

I am also including the code for the HTML file which you should call BreakEven.html.

 

<html>
<applet code="BreakEven.class" width=500 height=300>
</applet>
</html>

 

The GUI should look like the following.

 

 

Notice how the GUI responds to the changes in the radio buttons right when they are pressed, independently of other operations.  These are done through the RadioButtonHandler based on ItemListener.  The calculations are done based on the ActionListener associated with the command button.

There are other things about the code that we will discuss in class.