Ask Questions?

View Latest Questions

Advertisement


 
 

EZPanel
Posted on: July 24, 2006 at 12:00 AM
EZPanel makes layout much easier to write initially, and vastly easier to modify.

Java: EZPanel

Introduction

com.fredswartz.ezgui.EZPanel is a JPanel subclass using GridBagLayout, which provides a friendlier interface to GridBagLayout and avoids the error-prone tedium of dealing with GridBagConstraints. The one comparison I did with GridBagLayout resulted in a factor of 10 reduction in lines of coded needed for the layout (from 99 lines to 9 lines). Altho layout code is a small part of a program, it can be quite time-consuming to get it right and difficult to modify. EZPanel makes layout much easier to write initially, and vastly easier to modify.

Error checking. This EZPanel convenience layer between the user and GridBagLayout / GridBagConstraints also provides much needed error checks: it permits neither adding the same component twice nor overlapping components. Both of these error happen surprisingly frequently, and can be very difficult to find.

The following overview of EZPanel version 0.5 features should give you a good idea of what's available and some guidelines for using it. See Summary - EZPanel for a list of constructors and methods. More will be available as it nears official release.

This two column, three row EZPanel by default generates a standard border and gaps between components. It automatically resizes components (eg, the text fields) to the full width of their column (and height of row), a feature that can easily be changed.

import javax.swing.*;
import com.fredswartz.ezgui.*;

public class Form1 extends EZPanel{
    
    private JTextField m_firstTF   = new JTextField(12);
    private JTextField m_lastTF    = new JTextField(6);
    private JTextField m_addressTF = new JTextField(10);
 
    public Form1() { 
        row().add("First Name").add(m_firstTF);
        row().add("Last Name").add(m_lastTF);
        row().add("Address").add(m_addressTF);
    }
}

You may notice that the add() method can take a string parameter, which it converts into a JLabel, saving the need to create one yourself. Of course, if you want non-default attributes on a JLabel, just create one and add it like any other component.

A flexible grid / table / matrix

Grid. The layout is a table with individually sized rows and columns. Rows and columns are numbered from 1 (not 0). If you're familiar with HTML tables, you will find this very similar.

Row/column size is determined by the components in the row/column. The height of a row is the maximum of the preferred heights of all components in the row. A row can expand if any component is the row is expandable horizontally. Similarly for columns.

Area and alignment. Each component is added to an area on the grid. An area is defined by a row, column, width (in columns), and height (in rows). By default a component fills its area, and is resized, if necessary. Other alignments can be specified to center a component or align it to one or more edges.

Expansion of a row is possible vertically if any of the components can be expanded vertically. Autoexpansion is set for a couple of components: JTextArea and JScrollPane can expand vertically, and JTextField, JTextArea, and JScrollPane can expand horizontally. Autoexpansion can be disabled. In addition there are methods for explicitly setting the expansion characteristics. The same panel shown above can be resized, and the extra space is allocated to the text areas, not the border, gaps, or labels.

Gaps and borders

Gaps are automatically generated between content rows and columns. The default is 5 pixels, but it can be changed.

A border is generated around the panel (default 12 pixels), but can be set to a width or to a titled border.

Relative movement. Actual row and column numbers are rarely used because relative movements are normally specified. Following row(), relative movements default to left-to-right. Following col(), relative movements default to top-to-bottom. Relative movement allows easy modification of the layout.

Call chaining

EZPanel methods return this, so call chaining is easy, and often leads to more readable code if not carried to extremes.

This is laid out with a single line of code in the constructor.

row().add("First Name").add(m_firstTF).addHSpace(5).add("Last Name").add(m_lastTF);

I usually write a single layout statement for each row. If you don't like this style, write the calls as separate statements.

row();
add("First Name");
add(m_firstTF);
addHSpace(5);
add("Last Name");
add(m_lastTF);

The call addHSpace(5) adds 5 pixels of horizontal space. Vertical space can be added similarly.

Subpanels

In laying out a panel, try not to force all parts to fit into a grid if they don't really have any alignment relationship with each other. Use subpanels for parts where another layout would be more appropriate. It's fairly easy to create subpanels using EZPanel because of the call chaining.

The outer panel has three rows: the input panel, a button centered in its row, and the output panel.
public Convertor() {
    row().add(new EZPanel("Input")
           .row().add("Feet").add(m_feetTF).add("Inches").add(m_inchesTF)
           );
    row().center().add(m_convertBtn);
    row().add(new EZPanel("Output")
           .row().add("Centimeters").add(m_centimetersTF)
           .row().add("Light years").add(m_lightYearsTF)
           );
}

Explicitly specifying expansion

This BorderLayout clone shows how all expansion is in the center row and column (expandHV()).

public BorderClone() {
    border(0).setGap(0);
    row().width(3).add(m_northBtn);
    row().add(m_westBtn).expandHV().add(m_centerBtn).add(m_eastBtn);
    row().width(3).add(m_southBtn);
}

Automatic Expansion

Expandable components. The default in EZPanel is to automatically expand columns that contain a JTextField, JTextArea, or a JScrollPane. The same applies to rows except that JTextField don't cause automatic expansion. This mechanism is going to be expanded to other components and allow the user to have control of this.

Autoexpansion can be turned on and off by calling autoExpand(true/false). It's turned off in for this example because GridBagLayout makes the last column or row of an expandable display area expandable.

Where do we want extra horizontal space to be put? If we default to the last column, then the Close button will expand. We can turn autoexpansion off and and explicitly mark the JScrollPane as expandable vertically (expandV()). In the last row a horizontal spring is inserted as the second element in the last row to get all the extra space.

autoExpand(false);
row().widthRem().add(m_qodLabel);
row().widthRem().expandV().add(m_quoteSP);
row();
row().add(m_showOnStartupCB).addHSpring().add(m_nextBtn).add(m_closeBtn);

See how extra vertical space goes to the scroll pane, and extra horizontal space goes to the spring.

Alignment

A component will expand to fill its display area in the grid by default. If this doesn't give you the appearance you want, there are number of alignment options.

This is produced by the code below, which illustrates a number of features.

Expansion. To make rows and columns expand when the window is resized, there are explicit calls to expandV() (allow the row to expand vertically) and expandH() (allow the column to expand horizontally). If any element in a row or column is expandable, the entire row or column is expandable. In the example below, the second row of buttons really doesn't have to mark each with expandH() because it was sufficient to have done it once above. Unlike JTextFields and JTextAreas, there is not default automatic expansion for JButtons.

widthRem() specifies that the component takes up the remainder of the row. It is convenient to do this so that changes in the number of columns does not affect other parts of the layout.

Alignment. This shows calls to each of the compass direction alignment methods, as well as center().

Lines can be generated by calling addVLineI() or addHLine() These add a JSeparator object.

public AlignmentTest() {
    setGap(0);  // No gaps
    row().widthRem().add("Resize window to see components aligned in their cells.");
    row().addVSpace(5);  // Space below label.
    row().widthRem().addHLine();
    row().expandV().addVLine()
              .expandH()        .add(btnd).addVLine()
              .expandH().north().add(btnN).addVLine()
              .expandH().south().add(btnS).addVLine()
              .expandH().east() .add(btnE).addVLine()
              .expandH().west() .add(btnW).addVLine();
    row().widthRem().addHLine();
    row().expandV().addVLine()
              .expandH().center().add(btnC).addVLine()
              .expandH().northwest().add(btnNW).addVLine()
              .expandH().northeast().add(btnNE).addVLine()
              .expandH().southwest().add(btnSW).addVLine()
              .expandH().southeast().add(btnSE).addVLine();
    row().widthRem().addHLine();
    ;
}

Download

Beta version 0.5 (22 Feb 2005) is not quite ready for prime time. I've put it here only for beta testing.

Copyleft 2005 Fred Swartz MIT License
Advertisement


DMCA.com