Java Grids
Volume Number: | | 12
|
Issue Number: | | 11
|
Column Tag: | | Getting Started
|
Two Java Grid Layouts
By Dave Mark
Note: Source code files accompanying article are located on MacTech CD-ROM or source code disks.
Last month, we introduced the Java Layout Manager and saw the power of layouts combined with panels. This month, well look at two important layout classes, GridLayout and GridBagLayout, and present a series of applets that bring these two classes to life.
There is a newly reformatted set of Java API documentation, collectively known as the 1.0.2 API. If you dont already have this, go get it now. The URL is:
http://java.sun.com/doc/api_documentation.html
There are two Mac download links on this page. Though its bigger, you might try the .hqx file (as opposed to the .bin file). Im not sure why, but when I downloaded the .bin file and dropped it on StuffIt Expander, the final .sea file was corrupted. On the other hand, by the time you read this, the problem most likely will have been corrected.
GridLayout
As its name implies, the GridLayout lays out its components in a grid. GridLayout has two constructors:
public GridLayout( int rows, int cols );
This one creates a grid layout with the specified rows and columns. As youll see, GridLayout does the best it can to lay the current set of components out in this configuration. But what if you have too few components? Or too many? This months sample applets are ideal for experimenting.
The second constructor adds two new parameters:
public GridLayout( int rows, int cols,
int hgap, int vgap );
This version creates a grid layout with the specified rows and columns, but also lets you specify a minimal horizontal and vertical gap to appear between the components.
GridLayout is pretty straightforward. Heres a sample applet to take it for a spin.
Launch the CodeWarrior IDE and create a new Java Applet project called GridLayout.µ.
Create a new source code window, type in the following source code, save as GridLayout.Java, and add to it the project.
import java.awt.*;
public class MyGrid extends java.applet.Applet
{
public MyGrid()
{
setLayout( new GridLayout( 4, 4 ) );
add( new Button( 1 ) );
add( new Button( 2 ) );
add( new Button( 3 ) );
add( new Button( 4 ) );
add( new Button( 5 ) );
add( new Button( 6 ) );
add( new Button( 7 ) );
add( new Button( 8 ) );
add( new Button( 9 ) );
add( new Button( 10 ) );
add( new Button( 11 ) );
add( new Button( 12 ) );
add( new Button( 13 ) );
add( new Button( 14 ) );
add( new Button( 15 ) );
add( new Button( 16 ) );
}
}
Create a second source code window, type in the following HTML, save as GridLayout.html, and add it to the project as well.
<title>GridLayout</title>
<hr>
<applet codebase=GridLayout Files code=MyGrid.class width=200 height=200>
</applet>
<hr>
<a href=GridLayout.java>The source.</a>
Remove the two <replace me> files from the project (Apple-click on the files to select them, then hit option-delete).
Edit the project prefs, specifically, the Java Project pane. Set the Project Type popup to Class Folder and type GridLayout Files (without the quotes) as the Folder Name.
Its important that the Class Folder preference exactly match the codebase attribute in your HTML file.
Once all your source is in place, select Make from the Project menu to generate your class file.
To run your applet, drop the html file onto your Java Applet runner or Java-capable browser. Figure 1 shows my version running in a Netscape window.
Figure 1. The GridLayout applet, running in Netscape.
The GridLayout Source Code
Heres how the source code works. First comes the normal opening stuff, the import statement and class definition. The setLayout() statement creates a new GridLayout object with 4 rows and 4 columns, and makes it the current layout.
import java.awt.*;
public class MyGrid extends java.applet.Applet
{
public MyGrid()
{
setLayout( new GridLayout( 4, 4 ) );
Next, we create a series of 16 buttons and add them to the current frame.
add( new Button( 1 ) );
add( new Button( 2 ) );
add( new Button( 3 ) );
add( new Button( 4 ) );
add( new Button( 5 ) );
add( new Button( 6 ) );
add( new Button( 7 ) );
add( new Button( 8 ) );
add( new Button( 9 ) );
add( new Button( 10 ) );
add( new Button( 11 ) );
add( new Button( 12 ) );
add( new Button( 13 ) );
add( new Button( 14 ) );
add( new Button( 15 ) );
add( new Button( 16 ) );
}
}
Thats it! When you run the applet, your 16 buttons will appear in a 4 by 4 grid. The width and height of the buttons is determined by the width and height attributes in your HTMLs applet tag. Make the applet frame wider, the buttons will each be made wider. Make the frame taller, the buttons will each be made taller.
You can also affect the results by changing the parameters you pass to the GridLayout constructor. Experiment.
GridLayout, Version 2
Heres another GridLayout applet. This one uses all four constructor parameters, and includes a nifty little trick youll want to remember. First, heres the code:
import java.awt.*;
public class MyGrid extends java.applet.Applet
{
int numButtons;
String att;
public void init()
{
att = getParameter( NUMBUTTONS );
numButtons = Integer.valueOf(att).intValue();
setLayout( new GridLayout( 2, 20, 5, 20 ) );
for ( int i=1; i<=numButtons; i++ )
add( new Button( +i ) );
}
}
Next, heres the HTML:
<title>GridLayout</title>
<hr>
<applet codebase=GridLayout Files code=MyGrid.class width=400 height=200>
<param name=NUMBUTTONS value=16>
</applet>
<hr>
<a href=GridLayout.java>The source.</a>
Figure 2 shows the applet in action, running under Netscape. Lets take a look at this source.
Figure 2. Another GridLayout applet using all 4 parameters.
GridLayout 2 Source Code
This version of GridLayout.Java starts off in the same way, but does its creating in init() instead of in MyGrid(). This gives us access to the HTML parameters. Im not sure why getParameter() doesnt work from within MyGrid(), but Ill look into it.
import java.awt.*;
public class MyGrid extends java.applet.Applet
{
int numButtons;
String att;
If you look back at the HTML, youll see that we stuck in a parameter with the name NUMBUTTONS and a value of 16. We call getParameter() to pick up the parameter and Integer.valueOf(att).intValue() to convert the returned string to a number. Next, we create a new GridLayout using all 4 parameters and make it the current layout. Note that weve specified 2 rows and 20 columns, with 5 pixels horizontally and 20 pixels vertically between components. The Layout Manager uses the row value first for GridLayouts, so the fact that youve specified 20 columns really has no affect. Try using 0 for a column value.
public void init()
{
att = getParameter( NUMBUTTONS );
numButtons = Integer.valueOf(att).intValue();
setLayout( new GridLayout( 2, 20, 5, 20 ) );
Now for the cool trick. In our earlier example, we explicitly specified the name of each button using Strings like 1, 2, etc. In this case, we add the loop counter, i, to the null string to produce a string representation of the loop counter. Basically, weve forced Java to do the typecasting from number to String for us, since the + operator is expecting a String on both sides. Pretty cool, eh?
for ( int i=1; i<=numButtons; i++ )
add( new Button( +i ) );
}
}
The GridBagLayout
The GridLayout works pretty well if all your components are the same size. But, suppose you are working with all sorts of elements; some tall, some wide, whatever. In this case, the GridLayout wont work particularly well (itll waste a lot of screen real estate). Fortunately, there is a complex, grid-based class designed to handle variable sized components.
GridBagLayout and its sister class, GridBagConstraints, allow you to customize a layout that allows components to span multiple grid cells. The GridBagConstraints class features a number of variables, each designed to constrain any components added to the current GridBagLayout. Take a look at the GridBagConstraints class declaration:
public class Java.awt.GridBagConstraints
extends Java.lang.Object
implements Java.lang.Cloneable
{
// Fields
public int anchor;
public int fill;
public int gridheight;
public int gridwidth;
public int gridx;
public int gridy;
public Insets insets;
public int ipadx;
public int ipady;
public double weightx;
public double weighty;
// the anchor field has one of the following values
public final static int CENTER;
public final static int EAST;
public final static int NORTH;
public final static int NORTHEAST;
public final static int NORTHWEST;
public final static int SOUTH;
public final static int SOUTHEAST;
public final static int SOUTHWEST;
public final static int WEST;
// the fill field has one of the following values
public final static int BOTH;
public final static int HORIZONTAL;
public final static int NONE;
public final static int VERTICAL;
// default value for gridheight, gridwidth
public final static int REMAINDER;
// default value for gridx, gridy
public final static int RELATIVE;
// Constructors
public GridBagConstraints();
// Methods
public Object clone();
}
To use a GridBagLayout, youll create a GridBagLayout object along with a corresponding GridBagConstraints object, then make the GridBagLayout the current object. Next, youll set your GridBagConstraints fields to the settings you prefer. Now you are ready to start adding components to the current frame. All the added components will be formatted according to the current GridBagConstraints settings. Change the constraints settings and add some more components. The changed constraints only affect future components, not the components that were already added.
anchor determines where, within a cell, the component is placed. fill determines if the component is reissued to fill its cell and, if so, how. gridheight specifies the number of cells in a column. gridwidth specifies the number of cells in a row. REMAINDER is used to mark a component as the last in its row or column. RELATIVE is used to mark a component as next to last.
gridx and gridy allow you to specify where to place the component in the grid. A value of (0,0) will put the next component in the upper left corner. A value of RELATIVE will put the component either at the end of a row (in the case of gridx) or column (in the case of gridy).
insets specifies the number of pixels of padding on any side of a cell. ipadx and ipady allow you to specify the padding in pixels within a cell.
Finally, weightx and weighty allow you to specify how much horizontal and vertical space this component should consume when the available extra display area is divvied up between all the components in a row or column.
A GridBagLayout Example
There is really no way to truly appreciate the GridBagLayout without playing with an example. The following is one of the standard Sun applets, stripped down to make it as small as possible. Take some time to play with this applet. Change the constraints, experiment with all the fields and settings to see what they do.
Heres the source code:
import java.awt.*;
public class MyGridBag extends java.applet.Applet
{
public MyGridBag()
{
GridBagLayout gridBag = new GridBagLayout();
GridBagConstraintsconstraints =
new GridBagConstraints();
setLayout( gridBag );
constraints.fill = constraints.BOTH;
constraints.weightx = 1.0;
ConstrainedButton(Button1, gridBag, constraints );
ConstrainedButton(Button2, gridBag, constraints );
ConstrainedButton(Button3, gridBag, constraints );
constraints.gridwidth = constraints.REMAINDER;
ConstrainedButton(Button4, gridBag, constraints );
constraints.weightx = 0.0;
ConstrainedButton(Button5, gridBag, constraints );
constraints.gridwidth = constraints.RELATIVE;
ConstrainedButton(Button6, gridBag, constraints );
constraints.gridwidth = constraints.REMAINDER;
ConstrainedButton(Button7, gridBag, constraints );
constraints.gridwidth = 1;
constraints.gridheight = 2;
constraints.weighty = 1.0;
ConstrainedButton(Button8, gridBag, constraints );
constraints.weighty = 0.0;
constraints.gridwidth = constraints.REMAINDER;
constraints.gridheight = 1;
ConstrainedButton(Button9, gridBag, constraints );
ConstrainedButton(Button10, gridBag, constraints );
}
void ConstrainedButton( String title,
GridBagLayout layout, GridBagConstraints constraints )
{
Button button = new Button( title );
layout.setConstraints( button, constraints );
add( button );
}
}
Heres the HTML:
<title>GridBagLayout</title>
<hr>
<applet codebase=GridBagLayout Files code=MyGridBag.class width=400
height=100>
</applet>
<hr>
<a href=GridBagLayout.java>The source.</a>
Figure 3 shows the results of this applet, when run in Netscape.
Figure 3. The classic GridBagLayout applet from Sun.
Till Next Month...
As you go through the GridBagLayout source, pay attention to the use of REMAINDER and RELATIVE. Remember, you are marking a component as 2nd to last and last in its row or column. For example, Button4 should be the last in its row. Button6 should be RELATIVE (2nd to last) while Button7 should be REMAINDER (last). All the buttons should use a gridheight of 1 except for Button8, which will use a gridheight of 2. You get the idea.
Next month, well take a look at double-buffered animation, something that Java makes fairly easy to do. Till then, have a Happy Thanksgiving and save me a wishbone...