Flex your grid
layout
Tutorial Details:
Java Tip 121: Flex your grid layout
Java Tip 121: Flex your grid layout
By: By Bogdan Dorohonceanu
Extend java.awt.GridLayout to allow for multisized columns and rows
evelopers often need to create graphical user interfaces (GUIs) that have a matrix-type layout with columns of different widths or rows of different heights. Those layout cells are unequal in order to minimize the space occupied by the laid out components. In those cases, you have to use the GridBagLayout class, whose numerous GridBagConstraints settings cause your code to grow quickly. However, the GridLayout layout manager seems more appropriate. It can help you write short and readable code while laying out components in equally sized cells. In this tip, I extend GridLayout to create cells of unequal size.
Note: If you use a JTable to display information in a matrix, then check out " Java Tip 116: Set Your Table Options -- at Runtime! " Sonal Goyal with John D. Mitchell.
GridLayout2
The GridLayout2 extension is fairly simple. The GridLayout2 class inherits from java.awt.GridLayout and then redefines the public methods preferredLayoutSize() , minimumLayoutSize() , and layoutContainer() to account for multisized grid cells. I started from the source code of those methods in the java.awt.GridLayout class. (Download the complete source code in Resources .)
Set preferred size
Here is the preferredLayoutSize() method:
public Dimension preferredLayoutSize(Container parent) {
synchronized (parent.getTreeLock()) {
Insets insets = parent.getInsets();
int ncomponents = parent.getComponentCount();
int nrows = getRows();
int ncols = getColumns();
if (nrows > 0) {
ncols = (ncomponents + nrows - 1) / nrows;
}
else {
nrows = (ncomponents + ncols - 1) / ncols;
}
int[] w = new int[ncols];
int[] h = new int[nrows];
for (int i = 0; i < ncomponents; i ++) {
int r = i / ncols;
int c = i % ncols;
Component comp = parent.getComponent(i);
Dimension d = comp.getPreferredSize();
if (w[c] < d.width) {
w[c] = d.width;
}
if (h[r] < d.height) {
h[r] = d.height;
}
}
int nw = 0;
for (int j = 0; j < ncols; j ++) {
nw += w[j];
}
int nh = 0;
for (int i = 0; i < nrows; i ++) {
nh += h[i];
}
return new Dimension(insets.left + insets.right +
nw + (ncols-1) * getHgap(),
insets.top + insets.bottom +
nh + (nrows-1)*getVgap());
}
}
As you can see, the code is pretty straightforward. You first ensure that you have the right number of rows and columns to lay out the components. Then you find each component's preferred size. Finally, you compute each row's height as the row components' maximum height. You compute each column width as the maximum width of the row components. The preferred layout size also accounts for the horizontal and vertical gap sizes between components and the parent container insets.
Set minimum size
The code for minimumLayoutSize() is basically the same as preferredLayoutSize() , except you use the subcomponents' minimum size dimensions.
Set container size
The layoutContainer() method that follows accounts for each subcomponent's preferred size. The code also scales the component according to the parent container size, horizontal and vertical gap size, and insets:
public void layoutContainer(Container parent) {
synchronized (parent.getTreeLock()) {
Insets insets = parent.getInsets();
int ncomponents = parent.getComponentCount();
int nrows = getRows();
int ncols = getColumns();
if (ncomponents == 0) {
return;
}
if (nrows > 0) {
ncols = (ncomponents + nrows - 1) / nrows;
}
else {
nrows = (ncomponents + ncols - 1) / ncols;
}
int hgap = getHgap();
int vgap = getVgap();
// scaling factors
Dimension pd = preferredLayoutSize(parent);
double sw = (1.0 * parent.getWidth()) / pd.width;
double sh = (1.0 * parent.getHeight()) / pd.height;
// scale
int[] w = new int[ncols];
int[] h = new int[nrows];
for (int i = 0; i < ncomponents; i ++) {
int r = i / ncols;
int c = i % ncols;
Component comp = parent.getComponent(i);
Dimension d = comp.getPreferredSize();
d.width = (int) (sw * d.width);
d.height = (int) (sh * d.height);
if (w[c] < d.width) {
w[c] = d.width;
}
if (h[r] < d.height) {
h[r] = d.height;
}
}
for (int c = 0, x = insets.left; c < ncols; c ++) {
for (int r = 0, y = insets.top; r < nrows; r ++) {
int i = r * ncols + c;
if (i < ncomponents) {
parent.getComponent(i).setBounds(x, y, w[c], h[r]);
}
y += h[r] + vgap;
}
x += w[c] + hgap;
}
}
}
Again, you first ensure you have the right number of rows and columns to lay out the components. Then you compute the scaling factors on x and y coordinates as the ratios between the parent container's current height and width and the height and width of its preferred layout size. You find each component's preferred size and scale it using the scaling factors. You also compute each row's height as the maximum height of the scaled components in the row. You compute each column's width as the maximum width of the scaled components in the row. Finally, you lay out the components, considering the horizontal and vertical gap sizes between components and the parent container insets.
You could also scale the horizontal and vertical gap sizes. I will leave that as an exercise for the reader.
Compare GridLayout and GridBagLayout to GridLayout2
Figures 1, 2, and 3 show a random arrangement of 14 multisized JLabels using the three grid layouts: GridLayout , GridBagLayout , and GridLayout2 . Figure 3 shows the desired view.
Figure 1. Arrange 14 multisized JLabels with GridLayout layout manager
Figure 2. Arrange 14 multisized JLabels with GridBagLayout layout manager
Figure 3. Arrange the same 14 labels with GridLayout2 layout manager
As you can see, GridLayout sizes all matrix cells equally; GridBagLayout allows for each column/row cell to have its own width/height; and GridLayout2 looks the same as GridBagLayout without the extra baggage and hassle.
Create a compact panel
Figures 4, 5, and 6 show a sample password panel using the same three grid layouts: GridLayout , GridBagLayout , and GridLayout2 . As the above exercise, Figures 5 and 6 show the desired view: a compact change-password panel that doesn't consume as much screen space as Figure 4.
Figure 4. Sample change-password panel using GridLayout layout manager
Figure 5. Sample change-password panel using GridBagLayout layout manager
Figure 6. Same panel using GridLayout2 layout manager
These screenshots run the simple Test class shown below. Since GridLayout2 is a GridLayout subclass, you can use the same code to construct the samples for either layout (the Test class for GridBagLayout will follow):
public Test(GridLayout layout, JComponent[] component) {
super(layout.getClass().getName());
JPanel panel = new JPanel(layout);
//--- code needed to add the components
// less than using a java.awt.GridBagLayout
for (int i = 0; i < component.length; i ++) {
panel.add(component[i]);
}
//---
panel.setBorder(new EtchedBorder());
setContentPane(panel);
pack();
show();
}
The Test class extends the JFrame class, so you don't need to do anything special to create the window frame -- just make sure the constructor invokes super() appropriately. You then set the frame's content pane to be a JPanel instance, and the given layout to be the panel's layout manager. Then you add a component array to the panel by invoking the add() method for each component (see the loop iteration). Pretty simple. Finally, you pack() and show() the window.
Note that the test programs don't actually perform any actions. The test programs only show how the layout managers behave when the containers (whose layouts they manage) are resized.
On the other hand, GridBagLayout requires a separate routine to construct it. GridBagLayout requires more (convoluted) code to specify the constraints applied when laying out the components for the desired behavior:
public Test(int columns, Insets insets, JComponent[] component) {
super(GridBagLayout.class.getName());
GridBagLayout layout = new GridBagLayout();
JPanel panel = new JPanel(layout);
//--- code needed to add the components
GridBagConstraints constraints = new GridBagConstraints();
constraints.anchor = GridBagConstraints.WEST;
constraints.insets = insets;
constraints.fill = GridBagConstraints.BOTH;
for (int i = 0; i < component.length; i ++) {
constraints.gridx = i % columns;
constraints.gridy = i / columns;
layout.setConstraints(component[i], constraints);
panel.add(component[i]);
}
//---
panel.setBorder(new EtchedBorder());
setContentPane(panel);
pack();
show();
}
Here, you use a GridBagConstraints instance to create the desired layout. To simply lay out the components like in the previous example, use the constraints instance's gridx and gridy fields to specify each component's location in the grid layout. You can easily observe that using a java.awt.GridBagLayout manager requires writing more code; for example, the loop iteration contains three more lines. Also, as you'll see below, without more complex and convoluted constraint management, the GridBagLayout doesn't always behave as you would like when faced with functions like resizing windows.
Resizing
Now, let's resize Figures 1, 2, and 3 from the first exercise. (You can try this yourself by running the Test program in Resources .) Figures 7, 8, and 9 show the screenshots from my system.
Figure 7. Resize 14 multisized JLabels with a GridLayout layout manager
Figure 8. Resize 14 multisized JLabels with a GridBagLayout layout manager
Figure 9. Resize 14 multisized labels using a GridLayout2 layout manager
In resizing the first test batch, you can see that the GridLayout2 code in Figure 9 remains the only one that displays the desired view. I will let you resize the figures in the compact panel exercise. Note particularly the GridBagLayout example's poor behavior when you resize the window to something smaller than its desired size.
A more manageable matrix
In this tip
Read
Tutorial at: Click here to view the tutorial
Rate Tutorial: Flex your grid
layout
View Tutorial: Flex your grid
layout
Related
Tutorials:
JavaWorld article about
JavaCC
JavaWorld article about
JavaCC |
Add a file finder
accessory to JFileChooser - JavaWorld
Add a file finder
accessory to JFileChooser - JavaWorld |
Dynamic user interface is
only skin deep - JavaWorld May
2000
Dynamic user interface is
only skin deep - JavaWorld May
2000 |
Flex your grid
layout
Flex your grid
layout |
UI design with
Tiles and Struts
UI design with
Tiles and Struts |
SGLayout—a layout manager for the rest of us
SGLayout—a layout manager for the rest of us |
Speed up your
Swing GUI construction with better building blocks
Speed up your
Swing GUI construction with better building blocks |
SpeedJG - XML Builder
SpeedJG - XML based Java Swing GUI Builder |
WS-Specifications
WS-Specifications
The WS-Specifications build a composable architecture to form an environment for complex Web Service applications. Different vendors, such as BEA, IBM, Microsoft, RSA Security and SAP, have joined forces to lay the foundation of secure |
JGraph - The Java Graph Diagram Component
JGraph - The Java Graph Diagram Component
JGraphAddons is a drop-in functional module that adds powerful and configurable layout algorithms to your existing JGraphs. They include hierarchical, circular and tree layouts capable of giving your JGraph app |
JDock 1.1 - Swing docking framework
JDock is a pure java swing framework for managing, moving and resizing inner windows or components using a layout manager like a BorderLayout or a GridBagLayout.
|
Integrating Macromedia Flex with Java
Integrating Macromedia Flex with Java. Rich Internet Application (RIA) technologies are emerging to handle the limitations of the presentation layer. This article will teaches you Rich Internet Application development |
JSP templates. Use JSP templates to encapsulate Webpage layout and encourage modular design
Window toolkits typically provide a layout mechanism that positions widgets in a container. For example, AWT and Swing have layout managers, and VisualWorks Smalltalk has wrappers. This article presents a template mechanism for JSP that allows layout to b |
Reuse Tiles and Simplify UI
JavaServer Pages (JSP) technology supports application object reuse through includes, which allow other files (including other JSPs) to be sourced into a JSP file either at compile time or dynamically at application runtime. This is great for abstracting |
N1 Grid Service Provisioning System 5.0 Extends Features of Solaris Containers
A new version of the N1 Grid Provisioning System, scheduled for release in December, supports the Solaris Containers technology of the Solaris 10 Operating System. |
Developing Simple Struts Tiles Application
In this tutorial I will show you how to develop simple Struts Tiles Application. You will learn how to setup the Struts Tiles and create example page with it. |
Automating the Installation of an FC-Fabric SAN Booted System
Read about how an enterprise deployed and extended the Sun Data Center Reference Architecture to become its Service Delivery Platform. |
Developing Simple Struts Tiles
Application
Developing Simple Struts Tiles Application
Developing Simple Struts Tiles Application
Introduction
In this section I will show you how to develop simple Struts Tiles Application. You will learn how to setup the Struts Tiles and create example |
Enhance your end-user MDI experience with JExplose
Enhance your end-user MDI experience with *JExplose* ! Extremely simple to integrate with your existing Swing MDI products. |
DB Visual Architect for Eclipse
DB Visual Architect for Eclipse (DBVA-EC) is a full featured Object Relational Mapping (ORM) plugin for Eclipse that provides the industry\'s best round-trip code engineering support with Java. |
|
|
|