Easy to Learn Java: Programming Articles, Examples and Tips

Start with Java in a few days with Java Lessons or Lectures

Home

Code Examples

Java Tools

More Java Tools!

Java Forum

All Java Tips

Books

Submit News
Search the site here...
Search...
 
Search the JavaFAQ.nu
1000 Java Tips ebook

1000 Java Tips - Click here for the high resolution copy!1000 Java Tips - Click here for the high resolution copy!

Java Screensaver, take it here

Free "1000 Java Tips" eBook is here! It is huge collection of big and small Java programming articles and tips. Please take your copy here.

Take your copy of free "Java Technology Screensaver"!.

Writing GUI Layout Managers

JavaFAQ Home » Story by Dr. Kabutz Go to all tips in Story by Dr. Kabutz


Bookmark and Share

The Java Specialists' Newsletter [Issue 010] - Writing GUI Layout Managers

Author: Dr. Heinz M. Kabutz

You can subscribe from our home page: http://www.javaspecialists.co.za (which also hosts all previous issues, available free of charge Smile


Welcome to the 10th issue of "The Java(tm) Specialists' Newsletter". It has been a lot of fun writing these newsletters, especially since there is soooo much to write about in Java. Today I heard about some cases where in the JDK 1.3 the GUI threads get started which were not started in JDK 1.2.2. The effect is that command-prompt driven server code suddenly did not shut down anymore. An example is at the end of this newsletter thanks to James Pereira, Technical Director of Foundation Technologies, one of very few directors who still finds time to actually write Java code. He promised me a newsletter on class versioning with multiple ClassLoaders which should be very interesting. These differences in Java VMs harks of the days when we used to use C macros (#ifdef) to distinguish between versions of a 8087 FP chip used on a specific machine. At the rate Java gets changed, we have to be VERY careful to not rely on the existence of certain bugs.

But now, let's go back a few years, when JBuilder 2.0 had just come out. In those days, the GridBagLayout was not supported as well as in the later JBuilder versions, so we tried to avoid using it where possible. If you want to age quickly, try and maintain some GridBagLayout code written by someone else ...

A colleague of mine wanted to do a screen layout similar to BorderLayout but with different rules. The rules for BorderLayout are that the layout manager allocates preferred height for North and South components, then preferred width for East and West components, and the rest goes to Center. He wanted to have Left, Middle and Right components where the Middle took on its preferred width and Left and Right took on the remaining width shared equally. It would look like this:

OOOOOOOOOO    OOOOOOOOOO
OOOOOOOOOO    OOOOOOOOOO
OOOOOOOOOOaaaaOOOOOOOOOO
OOOOOOOOOOaaaaOOOOOOOOOO
OOOOOOOOOOaaaaOOOOOOOOOO
OOOOOOOOOO    OOOOOOOOOO
OOOOOOOOOO    OOOOOOOOOO

if we resized the screen, it would look like this:

OOOOOOOOOOOOOO    OOOOOOOOOOOOOO
OOOOOOOOOOOOOO    OOOOOOOOOOOOOO
OOOOOOOOOOOOOO    OOOOOOOOOOOOOO
OOOOOOOOOOOOOOaaaaOOOOOOOOOOOOOO
OOOOOOOOOOOOOOaaaaOOOOOOOOOOOOOO
OOOOOOOOOOOOOOaaaaOOOOOOOOOOOOOO
OOOOOOOOOOOOOO    OOOOOOOOOOOOOO
OOOOOOOOOOOOOO    OOOOOOOOOOOOOO
OOOOOOOOOOOOOO    OOOOOOOOOOOOOO

or

OOOOOOO    OOOOOOO
OOOOOOOaaaaOOOOOOO
OOOOOOOaaaaOOOOOOO
OOOOOOOaaaaOOOOOOO
OOOOOOO    OOOOOOO

We tried the standard way of combining layout managers with panels but were unable to do this layout. In those days without decent GridBagLayout tool support we were very hesitant to hack around with that monster layout. (Incidentally, the same type of layout is used by the jGuru crew to demonstrate that you need the GridBagLayout, so they also did not seem to get it right with normal layout managers.)

This caused me to try and write my own Layout Manager, which I called the WildLayoutManager, named after my ex-colleague, who is.

Writing your own GUI Layout Manager

It is actually increadibly straightforward writing your own layout manager. This layout manager took me one lunch time, i.e. less than 1 hour. Permanent employees at that company get free lunches, but as a contractor I had to pay, so I programmed instead of ate in those days. Nowadays I simply talk about work during lunch and charge the time Wink

Seriously, it really is easy. When I tell people that I've written my own LayoutManager to do the layout for me they get all boggle-eyed and start holding up crucifixes, garlic or search for the silver bullet. (Those in the know, know that there is no silver bullet for software development.)

//: WildLayoutManager.java
import java.awt.*;

public class WildLayoutManager implements LayoutManager {
  // these are the constraints possible with the WildLayoutManager
  public static final String LEFT = "Left";
  public static final String RIGHT = "Right";
  public static final String MIDDLE = "Middle";

  // We keep handles to three components, left, right and middle
  private Component left;
  private Component right;
  private Component middle;

  // we need to be able to add components.  if two components are added
  // with the same constraint we keep the last one
  public void addLayoutComponent(String name, Component comp) {
    if (LEFT.equals(name)) {
      left = comp;
    } else if (RIGHT.equals(name)) {
      right = comp;
    } else if (MIDDLE.equals(name)) {
      middle = comp;
    } else {
      throw new IllegalArgumentException(
        "cannot add to layout: unknown constraint: " + name);
    }
  }

  // here we remove the component - first find it!
  public void removeLayoutComponent(Component comp) {
    if (comp == left) {
      left = null;
    } else if (comp == right) {
      right = null;
    } else if (comp == middle) {
      middle = null;
    }
  }

  // The minimum dimension we're happy with is the preferred size
  // this could be more fancy by using the minimum sizes of each component
  public Dimension minimumLayoutSize(Container parent) {
    return preferredLayoutSize(parent);
  }

  // Here we work out the preferred size of the component, which is used
  // by methods such as pack() to work out how big the window should be
  public Dimension preferredLayoutSize(Container parent) {
    Dimension dim = new Dimension(0, 0);
    // get widest preferred width for left && right
    // get highest preferred height for left && right
    // add preferred width of middle
    int widestWidth = 0;
    int highestHeight = 0;
    if ((left != null) && left.isVisible()) {
      widestWidth = Math.max(widestWidth, left.getPreferredSize().width);
      highestHeight =
        Math.max(highestHeight, left.getPreferredSize().height);
    }
    if ((right != null) && right.isVisible()) {
      widestWidth = Math.max(widestWidth, right.getPreferredSize().width);
      highestHeight =
        Math.max(highestHeight, right.getPreferredSize().height);
    }
    dim.width = widestWidth * 2;
    dim.height = highestHeight;
    if ((middle != null) && middle.isVisible()) {
      dim.width += middle.getPreferredSize().width;
      dim.height = Math.max(dim.height, middle.getPreferredSize().height);
    }

    Insets insets = parent.getInsets();
    dim.width += insets.left + insets.right;
    dim.height += insets.top + insets.bottom;

    return dim;
  }

  // this is the brain of the layout manager, albeit rather small.
  // I told you this is straightforward...
  public void layoutContainer(Container target) {
    // these variables hold the position where we can draw components
    // taking into account insets
    Insets insets = target.getInsets();
    int north = insets.top;
    int south = target.getSize().height - insets.bottom;
    int west = insets.left;
    int east = target.getSize().width - insets.right;

    // we first find the width of the left and right components
    int widestWidth = 0;
    if ((left != null) && left.isVisible()) {
      widestWidth = Math.max(widestWidth, left.getPreferredSize().width);
    }
    if ((right != null) && right.isVisible()) {
      widestWidth = Math.max(widestWidth, right.getPreferredSize().width);
    }
    if ((middle != null) && middle.isVisible()) {
      widestWidth = Math.max(widestWidth,
        (east - west - middle.getPreferredSize().width) / 2);
    }

    // next we set the size of the left component equal to the widest width
    // and whole height, and we set the bounds from North-West corner
    if ((left != null) && left.isVisible()) {
      left.setSize(widestWidth, south - north);
      left.setBounds(west, north, widestWidth, south - north);
    }
    // next we set the size of right component equal to the widest width
    // and whole height, and we set the bounds from North-East corner
    if ((right != null) && right.isVisible()) {
      right.setSize(widestWidth, south - north);
      right.setBounds(east-widestWidth, north, widestWidth, south - north);
    }
    // lastly we set the size of the middle component equals to the
    // remaining width, which should be equal to the middle object's
    // preferred width and we set the height equal to the middle object's
    // preferred height
    if ((middle != null) && middle.isVisible()) {
      middle.setSize(east - west - widestWidth * 2,
        middle.getPreferredSize().height);
      middle.setBounds(
        west+widestWidth,
        north + (south - north - middle.getPreferredSize().height)/2,
        east - west - widestWidth * 2,
        middle.getPreferredSize().height);
    }
  }
}

You see, it really was quite simple. Here is an example frame that tries out the new layout manager:

//: WildLayoutExample.java
import java.awt.*;
import javax.swing.*;

public class WildLayoutExample extends JFrame {
  public WildLayoutExample() {
    super("WildLayoutExample");
    setSize(new Dimension(400, 300));
    getContentPane().setLayout(new WildLayoutManager());
    // construct the left panel
    JPanel leftPanel = new JPanel(new BorderLayout());
    leftPanel.add(new JLabel("Left Label"), BorderLayout.NORTH);
    leftPanel.add(new JTree(), BorderLayout.CENTER);
    // construct the middle panel
    JPanel middlePanel = new JPanel(new GridLayout(0,1,5,5));
    middlePanel.add(new JButton("Add >"), null);
    middlePanel.add(new JButton("<< Remove All"), null);
    // construct the right panel
    JPanel rightPanel = new JPanel(new BorderLayout());
    rightPanel.add(new JLabel("Right Label"), BorderLayout.NORTH);
    rightPanel.add(new JTextArea("jTextArea1"), BorderLayout.CENTER);
    // add the panels to the content pane using our new layout manager
    getContentPane().add(leftPanel, WildLayoutManager.LEFT);
    getContentPane().add(middlePanel, WildLayoutManager.MIDDLE);
    getContentPane().add(rightPanel, WildLayoutManager.RIGHT);
  }
  public static void main(String[] args) {
    WildLayoutExample frame = new WildLayoutExample();
    frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE); // JDK 1.3 !
    frame.setVisible(true);
  }
}

You could use the idea of custom layout managers to create all sorts of interesting layouts, such as a special form layout for designing forms for your business application. I do not see any particular problems with writing your own layout manager, especially if there is some layout that you want to use quite often. Just don't use absolute layouts, whatever you do!!!

And remember: let's be careful out there!

Heinz

---
Warning Advanced:
The following code exits normally under JDK 1.2.2 but "hangs" under JDK 1.3. Many thanks to James Pereira for pointing this out and sending me the code:

import java.awt.Cursor;
public class J23D {
  // This cursor class when loaded now starts the GUI event threads!
  private static Cursor s_waitCursor =
    Cursor.getPredefinedCursor(Cursor.WAIT_CURSOR);
  public void log(String _msg) {
    System.out.println(_msg);
  }
  public static void main ( String[] _args ) {
    new J23D().log("That's all folks!");
  }
} 


---


Copyright 2000-2003 Maximum Solutions, South Africa

Reprint Rights. Copyright subsists in all the material included in this email, but you may freely share the entire email with anyone you feel may be interested, and you may reprint excerpts both online and offline provided that you acknowledge the source as follows: This material from The Java(tm) Specialists' Newsletter by Maximum Solutions (South Africa). Please contact Maximum Solutions for more information.

Java and Sun are trademarks or registered trademarks of Sun Microsystems, Inc. in the United States and other countries. Maximum Solutions is independent of Sun Microsystems, Inc.


 Printer Friendly Page  Printer Friendly Page
 Send to a Friend  Send to a Friend

.. Bookmark and Share

Search here again if you need more info!
Custom Search



Home Code Examples Java Forum All Java Tips Books Submit News, Code... Search... Offshore Software Tech Doodling

RSS feed Java FAQ RSS feed Java FAQ News     

    RSS feed Java Forums RSS feed Java Forums

All logos and trademarks in this site are property of their respective owner. The comments are property of their posters, all the rest 1999-2006 by Java FAQs Daily Tips.

Interactive software released under GNU GPL, Code Credits, Privacy Policy