Comments
Richard Davies wrote: The UK has a good crop of technology pioneers in cloud computing - for example ElasticHosts, FlexiScale, Flexiant, OnApp - and also some strong government initiatives such as G-Cloud. We will have to see whether this kind of technical leadership converts into swift mass-market adoption or not.
Cloud Expo on Google News


2008 West
DIAMOND SPONSOR:
Data Direct
SOA, WOA and Cloud Computing: The New Frontier for Data Services
PLATINUM SPONSORS:
Red Hat
The Opening of Virtualization
GOLD SPONSORS:
Appsense
User Environment Management – The Third Layer of the Desktop
Cordys
Cloud Computing for Business Agility
EMC
CMIS: A Multi-Vendor Proposal for a Service-Based Content Management Interoperability Standard
Freedom OSS
Practical SOA” Max Yankelevich
Intel
Architecting an Enterprise Service Router (ESR) – A Cost-Effective Way to Scale SOA Across the Enterprise
Sensedia
Return on Assests: Bringing Visibility to your SOA Strategy
Symantec
Managing Hybrid Endpoint Environments
VMWare
Game-Changing Technology for Enterprise Clouds and Applications
Click For 2008 West
Event Webcasts

2008 West
PLATINUM SPONSORS:
Appcelerator
Get ‘Rich’ Quick: Rapid Prototyping for RIA with ZERO Server Code
Keynote Systems
Designing for and Managing Performance in the New Frontier of Rich Internet Applications
GOLD SPONSORS:
ICEsoft
How Can AJAX Improve Homeland Security?
Isomorphic
Beyond Widgets: What a RIA Platform Should Offer
Oracle
REAs: Rich Enterprise Applications
Click For 2008 Event Webcasts
SYS-CON.TV
Top Links You Must Click On


Creating a Custom Layout Manager
Creating a Custom Layout Manager

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).

    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.

    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 Joseph Cozad
    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.

  • In order to post a comment you need to be registered and logged in.

    Register | Sign-in

    Reader Feedback: Page 1 of 1

    Enterprise Open Source Magazine Latest Stories . . .
    Apache Deltacloud, the Red Hat-contributed ReSTful API that abstracts differences between clouds so services on any cloud can be managed – provided of course there’s a driver – has graduated from the Apache Foundation’s incubator and is now a full-fledged Top-Level Project (TLP). The...
    With Cloud Expo 2012 New York (10th Cloud Expo) just four months away, what better time to start introducing you in greater detail to the distinguished individuals in our incredible Speaker Faculty for the technical and strategy sessions at the conference... We have technical and st...
    AMD said late Tuesday that its chief sales officer Emilio Ghilardi had left the company and that CEO and president Rory Read is going to do his job while a replacement is sought. AMD didn’t say why Ghilardi left but it’s assumed Read wants his own people. Read is relatively new to th...
    During the lifespan of M3 (Monitis Monitor Manager) there has always been something lacking – timers. M3 execution procedure was outlined in this previous article. The execution mentioned in the latter was a one-time-execution, whereas server monitoring requires periodic invocati...
    Red Hat is putting its bought-in Gluster scale-out NAS storage technology, acquired in October, on the Amazon cloud. It’s styled Red Hat Virtual Storage Appliance for Amazon Web Services and other clouds are supposed to follow in short order.
    A new episode of the screencast series is now available at the OpenNebula YouTube Channel. This screencast demonstrates the new easily-customizable self-service portal for cloud consumers. Its aim is to offer a simplified access to shared infrastructure for non-IT end users. The scree...
    Subscribe to the World's Most Powerful Newsletters
    Subscribe to Our Rss Feeds & Get Your SYS-CON News Live!
    Click to Add our RSS Feeds to the Service of Your Choice:
    Google Reader or Homepage Add to My Yahoo! Subscribe with Bloglines Subscribe in NewsGator Online
    myFeedster Add to My AOL Subscribe in Rojo Add 'Hugg' to Newsburst from CNET News.com Kinja Digest View Additional SYS-CON Feeds
    Publish Your Article! Please send it to editorial(at)sys-con.com!

    Advertise on this site! Contact advertising(at)sys-con.com! 201 802-3021


    SYS-CON Featured Whitepapers
    ADS BY GOOGLE