HomeDigital EditionSys-Con RadioSearch Java Cd
Advanced Java AWT Book Reviews/Excerpts Client Server Corba Editorials Embedded Java Enterprise Java IDE's Industry Watch Integration Interviews Java Applet Java & Databases Java & Web Services Java Fundamentals Java Native Interface Java Servlets Java Beans J2ME Libraries .NET Object Orientation Observations/IMHO Product Reviews Scalability & Performance Security Server Side Source Code Straight Talking Swing Threads Using Java with others Wireless XML
 

A layout manager is an object that positions and resizes components within a display area according to a specific algorithm. The Java 1.2 AWT package provides 10 layout manager classes that can be used to accomplish this task. Each has a defined set of behaviors that organize components in a container. Each Java container instance is associated with an instance of one of these layout managers. By nesting one container/layout manager combination within another one, complex screen layouts can be implemented.

Sometimes the layout managers don't individually meet all the layout requirements needed to implement a particular GUI design. And in the case of complex layout designs, nesting container/layout manager combinations can produce code that is hard to understand. This article discusses what a layout manager is and how it is implemented in Java. Through a real-world example it demonstrates how a layout manager can be created to address any GUI layout situation without the need to nest container/layout manager combinations.

Layout Requirements
The GUI design for the example required that component locations be specified using a grid system of cells in which each cell was easily identifiable. The cells needed to be the same size and immutable, so an individual cell's size could be altered only if the container's overall size were also altered, and then only in proportion to the size change.

To accommodate a component larger than an individual cell, two or more cells could be combined horizontally or vertically (or both), creating a cell area addressed by the identity of the first cell in the upper-left corner of the area. This functionality is similar in design to the colspan and rowspan attributes in HTML tables.

Component locations within a cell area need to be specified in one of three ways:

  1. By a default value (the center of the cell area)
  2. By a predefined general location, such as top left, right, bottom right
  3. By a user-defined point within the cell area
In addition, components within the container could not lose their positions relative to the edges of the container when the container's size changed.

Components needed to maintain their size relative to the size of the container so that as the container enlarged or shrank, the components enlarged or shrank proportionally. Also, the specified initial size of the component could not be altered to fill a cell that was larger than the component; that is, component size integrity had to be maintained. The layout manager was required to provide a way to calculate the size of a component based on cell area size to assist in setting a component's size relative to the size of the container.

No limitation was placed on the number of components that could be added to a cell area. This meant that components could be overlapped and that overlapping should be done in a back-to-front manner. That is, component B added to the same container cell area as, and after, component A would lie on top of component A.

Design Choices
Because the requirements describe a method for laying out components based on a grid pattern, a designer might be led to use the GridLayout or GridBagLayout classes. While both provide a grid system, their limitations make them an inadequate solution for these requirements.

The GridLayout organizes components by dividing the display area into a grid of equal-sized cells numbered 0 to n from left to right. Each cell is filled in succession with only one component, thus providing no component overlap functionality. The GridLayout manager doesn't provide a way to address a specific cell in the grid in which to add a component. In addition, component sizes are normalized to fill the cell so that each cell maintains an equal size, regardless of the preset component size.

Similarly, the GridBagLayout organizes components into cells like the GridLayout, but provides greater flexibility in how they are displayed within each cell area by associating a separate constraints object (GridBagConstraints) with each component. Through the value manipulation of the GridBagConstraints object, each component can occupy more than one cell at a time. This is not a simple process, however, because it relies on a coordinate system rather than a single-cell ID number. For example, to add a component to a panel that implements a GridBagLayout manager, the programmer sets the instance variables, gridx and gridy, of the associated GridBagConstraints Object.

Alhough this manager provides a mechanism for a component to span more than one cell, it doesn't provide component overlap or a simple solution for defining where in a cell area the component should be located. Also, the GridBagLayout resizes components based on a combination of its associated constraints object, the minimum size of the component and the preferred size of the container. Setting a specific size for a button object can be overridden by the size calculated by the layout manager using the constraints object.

Java 1.2 introduces a new layout manager class called an OverlayLayout, which lays out a component based on the component's specified alignment value. While this class allows components to overlie each other, the programmer must set each component's alignmentX and alignmentY values so that the manager can calculate the component's size and location relative to the size of the enclosing container.

For this example, creating a custom layout manager solution centering around the general functionality of these three layout managers and incorporating the simplicity of HTML tables seemed appropriate. So how is this done? To begin designing a custom layout manager, it is first necessary to understand how it works and when.

Layout Manager Functionality
Java container classes in themselves provide no functionality supporting the positioning of components. Sun has separated this functionality into a set of layout manager classes. In turn, a layout manager controls only where components are displayed within the container, not what components are displayed. In effect, the container relies on the layout manager to place the components while the layout manager relies on the container to know what components are to be placed.

In Java, an object becomes a layout manager by implementing the methods declared in the java.awt.LayoutManager or java.awt.LayoutManager2 interfaces or both. The latter interface is new to Java 1.1, and extends the LayoutManager; no changes were made to these interfaces in Java 1.2. These interfaces describe 10 method signatures (behaviors) necessary for the implementing object to arrange components within a container. These behaviors can be grouped into six types:

  • Adding components
  • Removing components
  • Container layout
  • Invalidation
  • Size definition
  • Location definition
The first three types are the minimum behaviors needed to create a layout manager's functionality. In designing the add and remove behaviors, the most important consideration is that the layout manager needs to maintain and associate layout information on each component object maintained by the container object.

The following describes each group, what the associated methods do and when they are called by a container object. I assume the reader is familiar with how a layout manager is used by a container object, and knows that a Java applet or application never calls any of these implemented interface methods directly, but leaves these calls to be implemented by the container object itself.

Adding Components
When a component is added to a container using one of the container's add methods, the container adds a new component to the end of an array (n-1) of components that it maintains. The container then invalidates itself and calls one of the two registered layout managers' addLayoutCompontent(); methods (see Figure 1).

Figure 1
Figure 1:

These methods define how the layout manager will track the components that are added to the associated container:

public void addLayoutComponent(String position, Component comp);
public void addLayoutComponent(Component comp, Object constraints);

The layout manager should maintain the information passed by the string or constraints object to use when the container needs to be laid out (see "Container Layout"). This could be done with an array, vector or hash table.

The first method definition takes a String object that acts as a positional modifier indicating how or where the component should be laid out. For example, when a container object implements a BorderLayout object as its layout manager and a component is added to the center region of the container, the method Container.add("Center", comp); uses the string "Center" as a positional modifier.

The other method definition supports constraint-based layout management, which assumes that each component added to the container is associated with a separate constraint object that specifies how the component will be laid out. An example of this is seen in the GridBagLayout and the associated GridBagConstraints classes.

Removing Components
When the container's remove(Component comp); or removeAll(); method is called, the registered layout manager's removeLayoutComponent(); method is called. This method removes the corresponding component reference and any associated modifiers from the layout manager's stored references:

public void removeLayoutComponent(Component comp);

The container also removes the component object from the list of components it maintains. After this method has been called, the container calls the layout manager's invalidateLayout(); method (see "Container Invalidation").

Container Layout
When the container's doLayout(); method is called, the registered layout manager's layoutContainer(); method is called. This method executes the algorithm that sets the x and y coordinates' width and height of each component contained in the targeted container object:

public void layoutContainer(Container parent);

The algorithm designed to determine these values varies depending on what is required by the design of the layout manager. However, regardless of how the algorithm is designed, the following processes should happen:

  1. Retrieve a list of components maintained by the container using the Container.getComponents(); method.
  2. Test if the component is visible.
  3. Associate each visible component with its constraints, maintained by the layout manager.
  4. Generate the correct x,y width and height values for the component.
  5. Pass these values to the component's setBounds(); method. Note that the container size may have changed since the last time it was laid out.
After the container calls the layout manager's layoutContainer(); method, the container draws the components on the screen. The container does this by cycling through the container's array of components beginning at 0 and ending at n-1. When trying to facilitate overlapping, it is important to add the component to the beginning of the array, that is, index location 0. This can be accomplished by calling one of the container's add methods that includes an integer index value as one of its arguments, such as add(Component comp, Object obj, int index);. By specifying a different index value, the component can be inserted between two overlapping components.

Container Invalidation
Container invalidation occurs when the container's setLayout();, add();, remove(); or removeAll(); method is called. The container is marked as invalid and the registered layout manager's invalidateLayout(); method is then called. This method can define what actions to take, if any, when a container is marked invalid:

public void invalidateLayout(Container target);

Defining Size
When the container's getPreferredSize();, getMinimumSize(); or getMaximumSize(); is called, and if the container doesn't already have size information stored, the registered layout manager's preferredLayoutSize();, minimumLayoutSize(); or maximumLayoutSize(); method is called.

These methods can optionally define what the container's size should be after laying out all the objects and taking into consideration the container's insets:

public Dimension minimumLayoutSize(Container parent);
public Dimension preferredLayoutSize(Container parent);
public Dimension maximumLayoutSize(Container parent);

There's no guarantee that any one of these methods will be called before the layoutContainer(); method (see "Container Layout").

Defining Location
When the container's getAlignmentX(); or getAlignmentY(); is called, the registered layout manager's getLayoutAlignmentX(); or getLayoutAlignmentY(); is called. These methods can optionally define a percentage value representing the distance that a component should be from the edge of a container:

public float getLayoutAlignmentX(Container target);
public float getLayoutAlignmentY(Container target);

At minimum, a default return value of 0.5f for centered should be supplied.

Designing the RelationalGridLayout Manager
The resulting class from the example's design is the RelationalGridLayout. The RelationalGridLayout class, like the GridBagLayout, uses an associated constraints object (see CellConstraints.java Code) for each component added to the container. In addition, a LocationManager class was created to define nine locations within a cell area (see LocationManager.java Code) as public final static int types.

Unlike the GridBagConstraints class that gives direct exposure to its instance variables, the CellConstraints class contains four constructors that initialize a set combination of instance variables. This design allows for only four possible ways to define how the component should be laid out.

Each constructor takes an integer that indicates the cell number that contains the upper-left corner of the component. Three of the constructors take two integers, each representing the number of cells the component will span horizontally and vertically. These two values are used to represent the cell area in which the component will be placed. One of the constructors takes a java.awt.Point object that indicates the x and y coordinates of the component's upper-left corner within the cell area, and one of the constructors takes an integer that represents a predefined location within the cell area using one of several LocationManager values.

The following paragraphs discuss key points in the RelationalGridManager's implementation (see code listing).

The Constructor
To understand how the grid is represented, the constructor needs information about the targeted container's size, the number of columns and the number of rows. This information is used to initialize the instance variables, calculate the cell size and create a hash table used to hold the component layout information. Note that component layout information is stored in a private class object called ItemInfo (lines 7-19).

The Layout Manager's Behaviors
This design requires the implementation of only three of the 10 behaviors (the remaining are implemented either with empty bodies or with default return values; lines 43-50), which include:

Adding Components
The RelationalGridLayout implements the LayoutManager2 interface to support constraint-based modifiers. As such, it only implements the addLayoutComponent method that takes a reference to the component added by the container and Object (line 81). After testing for validity, the Object is cast to a CellConstraints type and assigned to a method variable (lines 87-89). In line 91 the size of the component is retrieved next and also saved in a method variable.

After the method variables have been assigned, the point values for the component's location are calculated (lines 94-111). If a point within the cell area is specified, the calcCellInset(); method is called (lines 185-192). This method takes the point, the cell area size, and the component's size to calculate the distances of the component's edges from the cell's edges. If, instead, a LocationManager value is specified, the calcInsets(); method is called (lines 203-278) to determine these same values based on the LocationManager value supplied. Once the component's insets are calculated for the cell area defined by the column and row values, the x,y coordinate or point value within the container's dimensions is calculated using the cell ID number and the component's insets (lines 98, 100 and 108).

Next, in lines 115-118, the method takes the calculated point and the component's size and converts this information into proportional ratios. These ratios are stored in an ItemInfo object and added to the instance's hash table keyed on a reference to the component (line 121).

Removing Components
No special processing is needed here other than to remove the component's display information from the hash table (lines 124-129).

Laying Out Components
The code to lay out the components in the container begins at line 131 by first determining if the container has changed in size since the last time the container was laid out. Line 136 compares the saved container's size (PanelSize) with the current size of the container retrieved in line 135. If the size has changed, the new size is saved and a new cell size is calculated.

Next, in line 142, a reference to the container's array of components is retrieved. Then the method loops through the array retrieving the component's modifier information from its associated ItemInfo object, converts it from ratios to actual pixels based on the current size of the container and then sends the x,y width and height information to the component using its setBounds(); method (lines 143-153).

The rest of the RelationalGridLayout implementation contains helper methods that assist in performing some of the calculations needed by the behaviors described. Two useful methods to note are:

  1. Calculating Cell Area Insets. The calcInsets(); (lines 221-296) method uses the supplied rowspan and colspan values to determine the size of the cell area in which the component will be located. Then, using the supplied LocationManager index value, the appropriate algorithm is selected to calculate and return the component's inset value for the cell area.
  2. Calculating Cell Area. The calcArea(); method (lines 54-59) is a public method used both internally and externally to assist in determining cell area size. By using this method as a public instance method, the size of the component can be preset relative to the size of the cell area in which it will be located.
Using the RelationalGridLayout
The TestCustomLayout applet demonstrates how the RelationalGridLayout is used. It also provides a way to interactively try out RelationalGridLayout functions like overlaying components, adding components to a specific place on the grid and maintaining the component's location when the container size changes. The TestCustomLayout applet's Add Component Screen is one example of how to use the RelationalGridLayout.

First, a sketch of the screen was created (see Figure 2). Then a grid pattern was derived by identifying the smallest element in the sketch; in this case the shape, size, color, cell number labels and their associated input fields were used to identify the size of the smallest cell. From this, an 8 x 8 grid pattern was created.

Figure 2
Figure 2:

The createPanel(); method in the AddCompPanel class provides the source defining the representation of the Add Component Screen (see AddCompPanel.createPanel Code). Much of the code is taken up by creating the individual components and specifying their characteristics. Line 6 creates the RelationalGridLayout object supplying the size of the panel and the number of columns and rows in the grid pattern. Notice that a method variable is created to hold the reference to the layout manager, as opposed to creating the reference directly in the setLayout(); method call in line 7. This was done so that we can use the layout manager's calcArea(); method to specify and preset component sizes.

Lines 10-104 create the instances of each component and set their characteristic values. Each component's size is set directly because the layout manager doesn't calculate the size, but calculates the component's proportional size (proportional to the container). Lines 17, 25, 28 and 96 use the layout manager's calcArea(); method to set these sizes relative to the sizes of their intended cell areas. For the shape, size, color and cell number labels; their fields; and the add button, a method variable (line 28) is assigned with the size calculated by the layout manager's calcArea(); method as two cells wide, one cell high, with a 10-pixel inset, just as the sketch shows.

Once created, the components are added to the container using the panel's add(Component comp, Object obj, int indx); method (lines 105-126). With each add a CellConstraints object is created, passing the cell number, the number of columns the component will span horizontally, the number of rows the component will span vertically and a location within the cell area. Some components have a value of 1 for either the column or row spans. The RelationalGridLayout does not assign a valid default value for these arguments. Also, the index value passed to the add method is 0, thus placing each component at the beginning of the container's component array.

Conclusion
Creating a custom layout manager in Java is a simple process that consists of implementing the appropriate layout manager interface that supports the container add methods that will be used, deciding which behaviors the layout manager will support and designing the algorithms that will calculate and track each component's location within the container. When one of the Java AWT default layout managers does not solve all the layout requirements, creating a custom layout manager may be the solution.

About the Author
Joseph Cozad is the Java technologist for Motorola's Advanced Media Platforms in Austin, Texas. As a Sun Microsystems certified Java developer, he serves as Motorola's corporate knowledge champion on Java technology. Prior to being a Java technologist, he established and coordinated online interactive systems. You can contact him at [email protected]

	

Listing 1:LocationManager.java code.
 
1  public interface LocationManager{ 
2    public final static int TOPLEFT = 0; 
3    public final static int TOPCENTER = 1; 
4    public final static int TOPRIGHT = 2; 
5    public final static int LEFT = 3; 
6    public final static int CENTER = 4; 
7    public final static int RIGHT = 5; 
8    public final static int BOTTOMLEFT = 6; 
9    public final static int BOTTOMCENTER = 7; 
10   public final static int BOTTOMRIGHT = 8; 
11 } 

Listing 2:CellConstraints.java code.
 
1  import java.awt.*; 
2 
3  class CellConstraints implements java.io.Serializable{ 
4 
5      protected int CellNum = -1; 
6      protected Point StartPoint = null; 
7      protected int Location = -1; 
8      protected int ColSpan = -1; 
9      protected int RowSpan = -1; 
10 
11    CellConstraints(int cell_num, int loc){ 
12       CellNum = cell_num; 
13       Location = loc; 
14    } 
15 
16    CellConstraints(int cell_num, int colspan, 
17                    int rowspan, Point loc){ 
18      CellNum = cell_num; 
19      ColSpan = colspan; 
20      RowSpan = rowspan; 
21      StartPoint = loc; 
22    } 
23 
24    CellConstraints(int cell_num, int colspan, 
25                    int rowspan, int loc){ 
26      CellNum = cell_num; 
27      ColSpan = colspan; 
28      RowSpan = rowspan; 
29      Location = loc; 
30    } 
31 
32    CellConstraints(int cell_num, int colspan, int rowspan){ 
33      CellNum = cell_num; 
34      ColSpan = colspan; 
35      RowSpan = rowspan; 
36    } 
37  } 

Listing 3: AddCompPanel.createPanel() code.
 
1   private void createPanel(){ 
2 
3     Font display_font = new Font("Helvetica", Font.BOLD, 14); 
4 
5     // Create the LayoutManager and set up this panel's basics. 
6     RelationalGridLayout layout_mgr = 
                new RelationalGridLayout(getSize(), 8, 8); 
7     setLayout(layout_mgr); 
8     setBackground(Color.black); 
9 
10    // Create the Purple Panel 
11    Panel purple_panel = new Panel(); 
12    purple_panel.setBackground(new Color(136,127,175)); 
13    purple_panel.setSize(layout_mgr.calcArea(8,2,-10)); 
                // layout_mgr.calcArea takes 8 cols, 2 rows, 10 pixel inset 
14 
15    // Create the Instruction Line 
16    Label instruct = new Label("Select the new component's 
                characteristics.", Label.CENTER); 
17    instruct.setSize(layout_mgr.calcArea(6,2,-10)); 
18    instruct.setForeground(Color.white); 
19    instruct.setBackground(new Color(136,127,175)); 
20    instruct.setFont(display_font); 
21 
22    // Create Long Blue Side Bar Panel. 
23    Panel side_bar = new Panel(); 
24    side_bar.setBackground(new Color(192,230,247)); 
25    side_bar.setSize(layout_mgr.calcArea(2,8)); 
                // layout_mgr.calcArea takes 2 cols, 8 rows, no inset. 
26 
27    // Create Shape, Size, Color, and Cell No. Fields and Labels 
28    Dimension field_size = layout_mgr.calcArea(2,1,-10); 
29 
30    // Shape: 
31    Label shape_label = new Label("Shape", Label.CENTER); 
32    shape_label.setFont(display_font); 
33    shape_label.setSize(field_size); 
34    shape_label.setBackground(new Color(192,230,247)); 
35 
36    shape = new Choice(); 
37    shape.setSize(field_size); 
38    shape.addItem("Square"); 
39    shape.addItem("Tall Rectangle"); 
40    shape.addItem("Long Rectangle"); 
41 
42    // Size: 
43    Label size_label = new Label("Size", Label.CENTER); 
44    size_label.setFont(display_font); 
45    size_label.setSize(field_size); 
46    size_label.setForeground(Color.white); 
47 
48    size = new Choice(); 
49    size.setBackground(Color.white); 
50    size.setSize(field_size); 
51    size.addItem("Small"); 
52    size.addItem("Medium"); 
53    size.addItem("Large"); 
54 
55    // Color: 
56    Label color_label = new Label("Color", Label.CENTER); 
57    color_label.setFont(display_font); 
58    color_label.setSize(field_size); 
59    color_label.setForeground(Color.white); 
60 
61    color = new Choice(); 
62    color.setBackground(Color.white); 
63    color.setSize(field_size); 
64    color.addItem("Red"); 
65    color.addItem("Black"); 
66    color.addItem("Blue"); 
67    color.addItem("Green"); 
68    color.addItem("Yellow"); 
69 
70    // Cell No.: 
71    Label cell_id_label = new Label("Cell No.", Label.CENTER); 
72    cell_id_label.setFont(display_font); 
73    cell_id_label.setSize(field_size); 
74    cell_id_label.setForeground(Color.white); 
75 
76    cell_id = new TextField("0"); 
77    cell_id.setBackground(Color.white); 
78    cell_id.setSize(35,30);  // Limit input to two numbers. 
79    cell_id.setEditable(true); 
80 
81    // Create Name Label and Field 
82    Label comp_label = new Label("Component's ", Label.RIGHT); 
83    comp_label.setFont(display_font); 
84    comp_label.setBackground(new Color(192,230,247)); 
85    Dimension comp_label_size = layout_mgr.calcArea(2,1,-10); 
86    comp_label.setSize(comp_label_size); 
87 
88    Label name_label = new Label("Name:", Label.CENTER); 
89    name_label.setFont(display_font); 
90    name_label.setForeground(Color.white); 
91    name_label.setSize(layout_mgr.calcArea(1,1,-10)); 
92 
93    comp_name = new TextField(DEFUALT_NAME); 
94    comp_name.setBackground(Color.white); 
95    comp_name.setEditable(true); 
96    Dimension name_field_size = layout_mgr.calcArea(5,1,-10); 
97    comp_name.setSize(name_field_size); 
98 
99    // Create Add Button 
100   Button add_button = new Button("Add"); 
101   add_button.addActionListener(panel_controller); 
102   add_button.setForeground(Color.black); 
103   add_button.setSize(field_size); 
104 
105   // Add Components to this Panel. 
106   // First three additions are to cell ID 0, each overlapping the other. 
107   add(side_bar, new CellConstraints(0,2,8,LocationManager.CENTER),0); 
108   add(purple_panel, new CellConstraints(0,8,2, 
                LocationManager.CENTER),0); 
109   add(instruct, new CellConstraints(0,8,2,LocationManager.CENTER),0); 
110 
111   add(shape_label, new CellConstraints(16,2,1, 
                LocationManager.BOTTOMCENTER),0); 
112   add(shape, new CellConstraints(24,2,1, LocationManager.TOPCENTER),0); 
113 
114   add(size_label, new CellConstraints(18,2,1, 
                LocationManager.BOTTOMCENTER),0); 
115   add(size, new CellConstraints(26,2,2, LocationManager.TOPCENTER),0); 
116 
117   add(color_label, new CellConstraints(20,2,1, 
                LocationManager.BOTTOMCENTER),0); 
118   add(color, new CellConstraints(28,2,1, LocationManager.TOPCENTER),0); 
119 
120   add(cell_id_label, new CellConstraints(22,2,1, 
                LocationManager.BOTTOMCENTER),0); 
121   add(cell_id, new CellConstraints(30,2,1, 
                LocationManager.TOPCENTER),0); 
122 
123   add(comp_label, new CellConstraints(32,2,2, LocationManager.RIGHT),0); 
124   add(name_label, new CellConstraints(34,1,2, LocationManager.LEFT),0); 
125 
126   add(comp_name, new CellConstraints(35,5,2,LocationManager.LEFT),0); 
127   add(add_button, new CellConstraints(54,2,2,LocationManager.CENTER),0); 
128 
129   doLayout(); 
130  } 

Listing 1
 
1   import java.awt.*; 
2   import java.util.Hashtable; 
3   import java.lang.Math; 
4   import LocationManager; 
5   import CellConstraints; 
6 
7   class ItemInfo implements java.io.Serializable{ 
8     double yratio = 0.0; // Row 
9     double xratio = 0.0; // Column 
10    double wratio = 0.0; // Item's width 
11    double hratio = 0.0; // Item's height 
12 
13    ItemInfo(double x, double y, double w, double h){ 
14      wratio = w; 
15      hratio = h; 
16      xratio = x; 
17      yratio = y; 
18    }  // Empty Constructor. 
19  } 
20 
21  public class RelationalGridLayout implements LayoutManager2, 
22            LocationManager, java.io.Serializable{ 
23 
24    // For the Grid 
25    protected int NumOfCols = 0;  // Number of columns in the grid. 
26    protected int NumOfRows = 0;  // Number of rows in the grid. 
27    protected Dimension CellSize = null; // The size of each cell. 
28    protected Dimension PanelSize = null; 
29    protected int CellSpacing = 0; 
30    protected int DefaultCellSpacing = 0; 
31 
32    protected Hashtable ComponentInfo = null; 
33 
34    // Constructor 
35    public RelationalGridLayout(Dimension size, int cols, int rows){ 
36      NumOfCols = cols; 
37      NumOfRows = rows; 
38      PanelSize = size; 
39      CellSize = calcCellSize(size); 
40      ComponentInfo = new Hashtable(); 
41    } 
42 
43    // Required Methods But Not Used 
44    public void addLayoutComponent(String name, Component comp){} 
45    public void invalidateLayout(Container target){} 
46    public float getLayoutAlignmentX(Container target){return 0.5f;} 
47    public float getLayoutAlignmentY(Container target){return 0.5f;} 
48    public Dimension maximumLayoutSize(Container target){return null;} 
49    public Dimension minimumLayoutSize(Container 
            target){return(target.getSize());} 
50    public Dimension preferredLayoutSize(Container 
            target){return(target.getSize());} 
51 
52    // Public Methods 
53    public Dimension calcArea(int cols, int rows, int padding){ 
54      Dimension size = new Dimension(getCellSize()); 
55      size.width = (size.width * cols) + padding; 
56      size.height = (size.height * rows) + padding; 
57      return(size); 
58    } 
59 
60    public Dimension calcArea(int cols, int rows){ 
61      return(calcArea(cols, rows, 0)); 
62    } 
63 
64    public Dimension getCellSize(){ // Get copy of cell's size. 
65      return(new Dimension(CellSize)); 
66    } 
67 
68    public int getCellSpacing(){ // Get the default spacing for the cell. 
69      return(CellSpacing); 
70    } 
71 
72    public void setCellSpacing(int i){ 
73      // Set spacing amount between component and cell edges. 
74      CellSpacing = i; 
75    } 
76 
77    public void resetCellSpacing(){ 
78      CellSpacing = DefaultCellSpacing; 
79    } 
80 
81    public void addLayoutComponent(Component comp, Object obj){ 
82      Insets insets = null; 
83      Point p = null; 
84      CellConstraints constraints = null; 
85 
86      // Cast the object to the correct type. 
87      if(obj instanceof CellConstraints){ 
88        constraints = (CellConstraints) obj; 
89      } 
90 
91      Dimension item_size = comp.getSize();  // Get the size of the item. 
92 
93      // Calculate point within container that the item is to be placed. 
94      if((constraints.ColSpan != -1) && (constraints.RowSpan != -1)){ 
95        if(constraints.StartPoint != null){ 
96          insets = calcCellInset(constraints.StartPoint, 
                  calcCellSize(constraints.ColSpan, constraints.RowSpan), 
                  item_size); 
97        }else if(constraints.Location > -1){ 
98          insets = calcInsets(constraints.Location, constraints.ColSpan, 
                 constraints.RowSpan, item_size); 
99        }else{ 
100         insets = calcInsets(CENTER, constraints.ColSpan, 
                 constraints.RowSpan, item_size); 
101       } 
102       // Convert cell_num to cell's x/y coordinate. 
103       p = calcStartPoint(constraints.CellNum, insets); 
104     }else{ 
105       if(constraints.CellNum != -1){ 
106         int cols = item_size.width/CellSize.width; 
107         int rows = item_size.height/CellSize.height; 
108         insets = calcInsets(CENTER, cols, rows, item_size); 
109         p = calcStartPoint(constraints.CellNum, insets); 
110       } 
111     } 
112 
113     // Create the proportional size and location for the component. 
114     // Get x, y, width and height's relationship to container's. 
115     double x = calcWidthRatio(p.x); 
116     double y = calcHeightRatio(p.y); 
117     double w = calcWidthRatio(item_size.width); 
118     double h = calcHeightRatio(item_size.height); 
119 
120     // Add the cell info to the hashtable. 
121     ComponentInfo.put(comp,new ItemInfo(x,y,w,h)); 
122   } 
123 
124   public void removeLayoutComponent(Component comp){ 
125     // Remove the component's info from hashtable. 
126     if(ComponentInfo.containsKey(comp)){ 
127       ComponentInfo.remove(comp); 
128     } 
129   } 
130 
131   public void layoutContainer(Container target){ 
132     ItemInfo cell; 
133     int x,y,w,h; 
134 
135     Dimension size = target.getSize(); 
136     if((PanelSize.width != size.width) || 
           (PanelSize.height != size.height)){ // Size may have changed. 
137       PanelSize = size; 
138       CellSize = calcCellSize(size); 
139    } 
140 
141     if(! ComponentInfo.isEmpty()){ 
142       Component[] comps = target.getComponents(); 
143       for(int i=0; i < comps.length; i++){ 
144         if(ComponentInfo.containsKey(comp)){ 
145           cell = (ItemInfo) ComponentInfo.get(comps[i]); 
146           // Convert relational values to real values 
147           x = (int) Math.round(size.width * cell.xratio); 
148           y = (int) Math.round(size.height * cell.yratio); 
149           w = (int) Math.round(size.width * cell.wratio); 
150           h = (int) Math.round(size.height * cell.hratio); 
151           comps[i].setBounds(x,y,w,h); 
152         } 
153       } 
154     } 
155   } 
156 
157   // Private & Protected Methods 
158   private double calcWidthRatio(int w){ 
159     return((double) w/PanelSize.width); 
160   } 
161 
162   private double calcHeightRatio(int h){ 
163     return((double) h/PanelSize.height); 
164   } 
165 
166   private Dimension calcCellSize(Dimension size){ 
167     int w = (int) Math.round((float) size.width/NumOfCols); 
168     int h = (int) Math.round((float) size.height/NumOfRows); 
169     return(new Dimension(w, h)); 
170   } 
171 
172   private Dimension calcCellSize(int colspan, int rowspan){ 
173     // Calculates the size of a cell given the colspan and rowspan. 
174     Dimension cell_size = new Dimension(CellSize); 
175     cell_size.width *= colspan; 
176     cell_size.height *= rowspan; 
177     return(cell_size); 
178   } 
179 
180   private int calcCellNum(int col, int row){ 
181     int cell_num = (NumOfCols * row) + col; 
182     return(cell_num); 
183   } 
184 
185   private Insets calcCellInset(Point p, Dimension cell_size, 
              Dimension item_size){ 
186     // Calculates the insets from a point given the size of an object. 
187    int left = p.x; 
188    int top = p.y; 
189    int right = cell_size.width - (p.x + item_size.width); 
190     int bottom = cell_size.height - (p.y + item_size.height); 
191     return(new Insets(top,left,bottom,right)); 
192   } 
193 
194   private Point calcStartPoint(int cell_num, Insets insets){ 
195     int row = cell_num/NumOfCols;  // row number. 
196     int col = (cell_num - (row * NumOfCols)); // column number. 
197     row = (row * CellSize.height) + insets.top; 
198     col = (col * CellSize.width) + insets.left; 
199     return(new Point(col,row)); 
200   } 


 

All Rights Reserved
Copyright ©  2004 SYS-CON Media, Inc.
  E-mail: [email protected]

Java and Java-based marks are trademarks or registered trademarks of Sun Microsystems, Inc. in the United States and other countries. SYS-CON Publications, Inc. is independent of Sun Microsystems, Inc.