Speed up your Swing GUI construction with better building blocks

Use two helper classes to reduce dialog development time

1 2 Page 2
Page 2 of 2

The class extends Swing's JPanel and has two additional methods init() and addItem(). The init() is called from the constructor and sets up GridBagLayout used by the panel. We have already shown the second method, addItem(), being used. This method creates a JLabel for the label text and then adds JLabel and passed-in item to the panel with the correct GridBagConstraints. The constraints are set up so that the label text and the item are properly aligned, and that the item extends to the dialog's right-hand edge.

A common dialog

The purpose of the class StandardDialog is to hold a data entry panel (such as a LabelledItemPanel), provide the standard Ok and Cancel buttons, and handle basic processing when the Ok or Cancel buttons are pressed.

The StandardDialog is modal, so a call to the show() method is a blocking call. After calling the show() method, the hasUserCancelled() method should be called to see if the user has cancelled the dialog by either pressing the Cancel button or the dialog close button (on the dialog frame).

StandardDialog can be used in two ways:

  • Create an instance of StandardDialog and add a data entry panel to the dialog
  • Subclass StandardDialog and add a data entry panel to the dialog during construction

An example of using StandardDialog directly is shown in the following code fragment:

// Create a data entry panel

StandardDialog dialog = new StandardDialog( (Frame)null, "Customer Details");

dialog.setContentPane(panel);

dialog.pack();

dialog.show();

if(!dialog.hasUserCancelled()) { // Process the data in the panel }

An example of using StandardDialog by subclassing is shown in the following code fragment:

public class CustomerDialog extends StandardDialog { private JTextField myCustomerCodeField = new JTextField(); private JTextField myNameField = new JTextField(); private JTextArea myAddressField = new JTextArea(3, 20); ...

private LabelledItemPanel myContentPane = new LabelledItemPanel();

public CustomerDialog() { init(); }

private void init() { setTitle("Customer Dialog");

myContentPane.setBorder(BorderFactory.createEtchedBorder());

myContentPane.addItem("Customer Code", myCustomerCodeField); myContentPane.addItem("Name", myNameField); myContentPane.addItem("Address", new JScrollPane(myAddressField, JScrollPane.VERTICAL_SCROLLBAR_ALWAYS, JScrollPane.HORIZONTAL_SCROLLBAR_NEVER)); ...

setContentPane(myContentPane); }

public CustomerData getCustomerData() { CustomerData customerData = new CustomerData();

customerData.myCustomerCode = myCustomerCodeField.getText(); customerData.myName = myNameField.getText(); customerData.myAddress = myAddressField.getText(); ...

return customerData; } }

// A method in some class using the CustomerDialog { CustomerDialog dialog = new CustomerDialog();

dialog.pack();

dialog.show();

if(!dialog.hasUserCancelled()) { CustomerData customerData = dialog.getCustomerData();

// Process the data } }

If data validation needs to be performed before the dialog closes, then the isValidData() method should be overridden. An example of this is given below:

protected boolean isValidData() { if(myCustomerCodeField.getText().equals("")) { JOptionPane.showMessageDialog(this, "Please enter a Customer Code", "Blank Customer Code", JOptionPane.WARNING_MESSAGE);

myCustomerCodeField.requestFocus();

return false; }

if(myNameField.getText().equals("")) { JOptionPane.showMessageDialog(this, "Please enter a Name", "Blank Name", JOptionPane.WARNING_MESSAGE);

myNameField.requestFocus();

return false; }

return true; }

StandardDialog behind the scenes

This section explains how StandardDialog works. The appearance and behavior of StandardDialog is set up in the init() method shown below:

private void init() { setModal(true); setDefaultCloseOperation(DO_NOTHING_ON_CLOSE);

// Setup the internal content pane to hold the user content pane // and the standard button panel

JPanel internalContentPane = new JPanel();

internalContentPane.setLayout( new BorderLayout(COMPONENT_SPACING, COMPONENT_SPACING));

internalContentPane.setBorder( BorderFactory.createEmptyBorder(COMPONENT_SPACING, COMPONENT_SPACING, COMPONENT_SPACING, COMPONENT_SPACING));

// Create the standard button panel with "Ok" and "Cancel"

Action okAction = new AbstractAction("Ok") { public void actionPerformed(ActionEvent actionEvent) { if(isValidData()) { myIsDialogCancelled = false;

dispose(); } } };

Action cancelAction = new AbstractAction("Cancel") { public void actionPerformed(ActionEvent actionEvent) { myIsDialogCancelled = true;

dispose(); } };

JPanel buttonPanel = new JPanel();

buttonPanel.setBorder(BorderFactory.createEmptyBorder(0, 0, 0, 0));

buttonPanel.add(new JButton(okAction)); buttonPanel.add(new JButton(cancelAction));

internalContentPane.add(buttonPanel, BorderLayout.SOUTH);

// Initialise the user content pane with a JPanel

setContentPane(new JPanel(new BorderLayout()));

super.setContentPane(internalContentPane);

// Finally, add a listener for the window close button. // Process this event the same as the "Cancel" button.

WindowAdapter windowAdapter = new WindowAdapter() { public void windowClosing(WindowEvent windowEvent) { myIsDialogCancelled = true;

dispose(); } };

addWindowListener(windowAdapter); }

The init() is called from the constructor. The method does the following:

  • Sets up actions for the Ok and Cancel buttons. These actions set the field myIsDialogCancelled to indicate which button has been pressed and then call dispose, which causes the show() method to unblock. The method hasUserCancelled() checks the value of myIsDialogCancelled as shown in the usage example.

  • Builds the two buttons and ties them to the actions. The two buttons are added to a panel, and the panel is then added to the dialog.

  • Sets up an event handler for the close window event. This event handler performs the same processing as the action for the Cancel button.

Note: Download the full source code for LabelledItemPanel and StandardDialog from Resources.

Advantages

We have saved substantial development time with this approach. The average time spent building a panel using an IDE GUI builder was between one and three hours depending on the number of fields on the panel. Using our LabelledItemPanel class, the panels were constructed in less than 20 minutes. We have used LabelledItemPanel 35 times.

The other advantages of this approach include:

  • Absolutely no layout constraints need to be understood or specified. This allows developers with little Swing knowledge to quickly construct user interfaces.

  • Consistency of data entry panels and dialogs, both for users and developers.

  • Reordering the fields is simple. You only need to reorder. addItem() calls. You do not have to adjust constraints on all of the reordered fields and labels.

  • The layout and properties for all entry panels can be changed centrally.

  • No unnecessary references are held for labels.

Further enhancements

Our simple classes could be further enhanced to include the following:

  • LabelledItemPanel could support a read-only flag for viewing, but not editing, data
  • LabelledItemPanel could support I18N by passing in a ResourceBundle key for a label instead of passing in text already internationalized

The need for speed

We have described an approach that speeds up the construction of Swing user interfaces by using better building blocks than ones available in the Swing libraries. It relies on two classes: LabelledItemPanel and StandardDialog. The essence of our approach was to identify repetitive elements of GUIs that could be encapsulated in easily reusable code.

David Fraser is a senior software engineer specializing in user interfaces and has worked on software in the financial, telecommunications, and interactive television industries. He has been developing user interfaces for eight years on DOS, Windows, and Unix platforms, using the BASIC, C, C++, VB, and Java languages. He has been developing user interfaces with Swing for the last three years. Michael Harris is a software architect. He has spent a number of years developing application frameworks for Internet and network management applications for defense, telecommunications, and television industries. He is always looking for ways to make his fellow programmers' lives easier. He has been using Java for the past five years.

Learn more about this topic

1 2 Page 2
Page 2 of 2