The Source for Java Technology Collaboration


Home | Changes | Index | Search | Go

PageLayout

This is the sample code for the example on John O'Conner's blog for a layout manager challenge using the PageLayout layout manager. Note that a single layout manager is used for a somewhat typical but nontrivial layout. The image of the GUI can be found on the the Sourceforge site for the project. (Please note that the code has been modified since the first posting, and now the class GridRow is used to create the rows of the grid. The modified code is somewhat more compact.)

The main motivation for the development of PageLayout was to provide a layout manager that can be used for all cases with a uniform API, without the need for mixing and matching two or more of the many layout managers available both within the standard Java distribution and outside, the latter from many third parties. PageLayout first started as a simple project to (a) hide the somewhat intricate details of the GroupLayout by allowing the programmer to layout components only once ( GroupLayout requires you to do so twice, once along each direction) in nested rows and columns and (2) from this specification transparently generate the calls to the GroupLayout API for actually managing the layout behind the scenes. During this development, a more general algorithm suggested itself, and the PageLayout was born.

PageLayout generally requires the programmer to think only in terms of familiar geometrical concepts like rows, columns, grids, and alignments. There is almost never any need to explicitly use numerical values for the locations and the sizes of components or to specify which components should be allowed to grow or shrink as the window resizes. A well documented API is available, and there is no need to encode what you want in terms of cryptic string commands. In addition, it's open, and the source code is available for free.

example10.gif


import pagelayout.*;
import static pagelayout.Cell.*;
import static pagelayout.CellGrid.*;
import java.awt.*;
import javax.swing.*;
import javax.swing.event.*;
import javax.swing.border.*;
public class AddressBookDemo
{
   
   public static void createGUI()
   {
      JFrame frame=new JFrame();
      frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
      Container container=frame.getContentPane();

      JLabel lastName=new JLabel("Last Name");
      JTextField lastNameE=new JTextField("Martian",20);
      JLabel firstName=new JLabel("First Name");
      JTextField firstNameE=new JTextField("Marvin",20);
      JLabel phone=new JLabel("Phone");
      JTextField phoneE=new JTextField("805-123-4567",20);
      JLabel email=new JLabel("Email");
      JTextField emailE=new JTextField("marvin@wb.com",20);
      JLabel address1=new JLabel("Address1");
      JTextField address1E=new JTextField("1001001010110 Martian Way ",30);
      JLabel address2=new JLabel("Addres2");
      JTextField address2E=new JTextField("Suite 101101011 ",30);
      JLabel city=new JLabel("City");
      JTextField cityE=new JTextField("Ventura ",20);
      JLabel state=new JLabel("State");
      JTextField stateE=new JTextField("CA ",20);
      JLabel county=new JLabel("County");
      JTextField countyE=new JTextField("USA",20);
      JLabel postal=new JLabel("Postal Code");
      JTextField postalE=new JTextField("93001 ",20);
      JButton b1=new JButton("New");
      JButton b2=new JButton("Delete");
      JButton b3=new JButton("Edit");
      JButton b4=new JButton("Save");
      JButton b5=new JButton("Cancel");
      JList list=new JList(new StringListModel());
      list.setBorder(BorderFactory.createLoweredBevelBorder());
      JScrollPane scroll=new JScrollPane(list);

      
      // First row
      GridRow row1=new GridRow(lastName,lastNameE,firstName,firstNameE);

      // Second row. Note the special treatment for the email part of the form.
      GridRow row2=new GridRow(phone,phoneE);
      row2.add(new Row(NO_ALIGNMENT,CENTER,email,emailE)).span(2);
      
      // Third row
      GridRow row3=new GridRow(address1,address1E);
      row3.span(3);

      // Fourth row
      GridRow row4=new GridRow(address2,address2E);
      row4.span(3);

      // Fifth row
      GridRow row5=new GridRow(city,cityE);

      // Sixth row
      GridRow row6=new GridRow(state,stateE,postal,postalE);

      // Seventh row
      GridRow row7=new GridRow(county,countyE);


      // Create the grid.
      CellGrid grid=CellGrid.createCellGrid(row1,row2,row3,
                  row4,row5,row6,row7);

      // Alignment of the cells in the grid.
      grid.setAlignments(
         new int[][]{{RIGHT,NO_ALIGNMENT,LEFT,NO_ALIGNMENT}},
         new int[][]{{CENTER,CENTER,CENTER,CENTER}});

      // The row of buttons. JUSTIFIED should be changed to LEFT, CENTER or RIGHT 
      //depending upon the requirement.
      Row buttons=new Row(Row.JUSTIFIED,Row.CENTER,b1,b2,b3,b4,b5);

      // Make a column using the grid and the row of buttons.
      Column col=new Column(grid,buttons);

      // Make the buttons of equal width
      col.linkWidth(b5,new Component[]{b1,b2,b3,b4},1);

      
      // Create the row containing the list and the column with the
      // grid and the button row. This row is  the top level
      // cell.
      Row row =new Row();
      row.add(scroll).add(col);

      // Create the layout.
      row.createLayout(container);

      frame.pack();
      frame.setSize(frame.getPreferredSize());
      frame.show();
   }
   private static class StringListModel implements ListModel
   {
      public void addListDataListener (ListDataListener l){}
      String[] names={
         "Bunny Buggs",   
         "Cat, Sylvester ",   
         "Coyote, Willie E. ",   
         "Devil, Tasmanian",   
         "Duck, Daffy",   
         "Fudd, Elmer",   
         "LePew, Pepe",   
         "Martian, MArvin"
      };
      public Object getElementAt(int index)
      {
         if(index<names.length)return names[index];
         return new String("                       ");
      }
      public int getSize(){ return 30;}
      public void removeListDataListener (ListDataListener l){}
   }

   public static void main(String[] args)
   {
      createGUI();
   }
}

I have added a GridRows class to the API that makes it even simpler to construct a grid. Here is the pertinent part of the code above. The full code is available here, and can be downloaded as a part of the distribution.

      
      // All the rows are added to the grid by calling the newRow method of this object.
      GridRows rows=new GridRows();
      
      // First row 
      rows.newRow().add(lastName,lastNameE,firstName,firstNameE);

      
      // Second row. Note the special treatment for the email part of the form.
      rows.newRow().add(phone,phoneE)
          .add(new Row(NO_ALIGNMENT,CENTER,email,emailE)).span(2);
      
      
      // Third row 
      rows.newRow().add(address1,address1E).span(3);

      // Fourth row 
      rows.newRow().add(address2,address2E).span(3);
      
      // Fifth row 
      rows.newRow().add(city,cityE);
      
      // Sixth row 
      rows.newRow().add(state,stateE,postal,postalE);

      // Seventh row
      rows.newRow().add(county,countyE);

      // Create the Grid from the GridRows object
      CellGrid grid=rows.createCellGrid();;

      // Alignment of the cells in the grid.
      grid.setAlignments(
         new int[][]{{RIGHT,NO_ALIGNMENT,LEFT,NO_ALIGNMENT}},
         new int[][]{{CENTER,CENTER,CENTER,CENTER}});

      // The row of buttons.JUSTIFIED should be changed to LEFT, CENTER or RIGHT 
      //depending upon the requirement.
      Row buttons=new Row(Row.JUSTIFIED,Row.CENTER,b1,b2,b3,b4,b5);

      // Create a column using the grid and the row of buttons.
      Column col=new Column(grid,buttons);

      // Constrain the buttons to be of equal width
      col.linkWidth(b5,new Component[]{b1,b2,b3,b4},1);

      
      // Create the row containing the list and the column with the
      // grid and the button row. This row is  the top level
      // cell.
      Row row =new Row();
      row.add(scroll).add(col);

      // Create the layout.
      row.createLayout(container);

Answers to questions

  • Can it align components across other components? For example, can it both left and right align the 'phone' text field with the 'city' text field on the other side of the two address fields?

    Yes.

  • Does your manager know about platform specific gaps and spacing among components. Just curious.

    No.

  • Can the text fields, labels, etc grow and shrink when the text is localized?

    Yes. By default, the components for which the preferred and maximum width or height are different are allowed to grow in that direction. JDK 1.5 automatically provides such information so that, for example, buttons are fixed in size, and text fields can grow. So normally the programmer does not have to do anything to control this behavior. However, the API provides simple methods to override this behavior.

  • Just how much code do I have to write? Java code? Declarative XML?

    You have to write Java code.

  • Can I nest panels?

    Yes. But in most cases it is not necessary.

  • Can it align components along a baseline. For example, the new GroupLayout can align labels and text fields along the text baseline...that's nice.

    No. But this can be added easily. (Added on 10/22/06. done. The latest version 1.11 has support for baseline alignment.)

  • What's the licensing?

    LGPL. It's available for free.

  • Will this work for bi-directional locales? How difficult would it be to mirror the layout for a right-to-left locale?

    No. If there is a need I can add this.

  • What else should we evaluate?

    As for any software, (1) generality of the underlying algorithm so that it is able to handle most common cases, (2) availability of good documentation and tutorial, (3) ease with which a typical programmer can understand the concepts embodied in the API, and (4) simplicity and maintainability of the code that uses the layout manager.

    For this particular case we also need to look at whether the default behavior of the components when windows are resized is good enough for most cases.

    The purpose of a Layout Manager must also be to make programming GUIs so simple that most of the time GUI builders are not even necessary.

-- Main.joconner - 11 Oct 2006 Modified by varan.

Topic PageLayout . { Edit | Ref-By | Printable | Diffs r11 < r10 < r9 < r8 < r7 | More }
 XML java.net RSS

Revision r11 - 31 Oct 2006 - 04:52:04 - Main.varan
Parents: 3rdParty