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

Swing Chapter 4. (The basics) Layout Managers. Easy for reading, Click here!

Custom Search
Swing Chapter 4. (The basics) Layout Managers. Easy for reading, Click here!

[ Return to Swing (Book) ]

Page: 5/7 



Previous Page Previous Page (4/7) - Next Page (6/7) Next Page
Subpages: 1. Layouts overview 
2. Comparing common layout managers
3.
Using GridBagLayout
4. Choosing the right layout
5. Custom layout manager: part I -Label/field pairs
6. Custom layout manager: part II - Common interfaces
7. Dynamic layout in a JavaBeans container

4.5    Custom layout manager: part I -Label/field pairs

This example is intended to familiarize you with developing custom layouts. You may find this knowledge useful in cases where the traditional layouts are not satisfactory or are too complex. In developing large scale applications it is often more convenient to build custom layouts, such as the one we develop here, to help with specific tasks. This often provides increased consistency, and may save a significant amount of coding in the long run.

The example in the previous section highlighted a problem: what is the best way to lay out input field components (e.g. text fields, combo boxes, etc.) and their corresponding labels? We have seen that it can be done using a combination of several intermediate containers and layouts. This section shows how we can simplify the process by using a custom-built layout manager. The goal is to construct a layout manager that knows how to lay out labels and their associated input fields in two columns, allocating the minimum required space to the column containing the labels, and using the remainder for the column containing the input fields.

First we need to clearly state our design goals for this layout manager, which we will appropriately call DialogLayout. It is always a good idea to reserve plenty of time for thinking about your design. Well-defined design specifications can save you tremendous amounts of time in the long run, and can help pinpoint flaws and oversights before they arise in the code. (We strongly recommend that adopting a design specification stage become part of your development regimin.)

DialogLayout specification:

1. This layout manager will be applied to a container that has all the necessary components added to it in the following order: label1, field1, label2, field2, etc. (Note that when components are added to a container they are tracked in a list. If no index is specified when a component is added to a container it will be added to the end of the list using the next available index.  As usual this indexing starts from 0. A component can be retreived by index using the getComponent(int index) method.) If the labels and fields are added correctly, all even numbered components in the container will correspond to labels, and all odd numbered components will correspond to input fields.

2. The components must be placed in pairs forming two vertical columns.

3. Components making up each pair must be placed opposite one another (i.e. label1 field1). Each pair's label and field must receive the same preferable height, which should be the preferred height of the field.

4. Each left component (labels) must receive the same width. This width should be the maximum preferable width of all left components.

5. Each right component (input fields) must also receive the same width. This width should occupy all the remaining space left over from that taken by the left components column.

The code below introduces our custom DialogLayout class which satisfies the above design specification. This class is placed in its own package named dl. The code used to construct the GUI is alsmot identical to that of the previous example. However, we now revert back to variant 1 and use an instance of DialogLayout instead of a GridLayout to manage the p1r JPanel.

Figure 4.16 Using DialogLayout - custom layout manager part I

<<file figure4-16.gif>>

The Code: FlightReservation.java

see \Chapter4\4

import java.awt.*;

import java.awt.event.*;

import javax.swing.*;

import javax.swing.border.*;

import javax.swing.event.*;

import dl.*;

public class FlightReservation extends JFrame

{

  public FlightReservation() {

    super("Flight Reservation Dialog [Custom Layout]");    

    // Unchanged code from section 4.4

    // Variant 1

    JPanel p1r = new JPanel();

    p1r.setBorder(new EmptyBorder(10, 10, 10, 10));

    p1r.setLayout(new DialogLayout(20, 5));

    p1r.add(new JLabel("Date:"));

    p1r.add(new JTextField());

    p1r.add(new JLabel("From:"));

    JComboBox cb1 = new JComboBox();

    cb1.addItem("New York");

    p1r.add(cb1);

    p1r.add(new JLabel("To:"));

    JComboBox cb2 = new JComboBox();

    cb2.addItem("London");

    p1r.add(cb2);

    p1.add(p1r);

    getContentPane().add(p1, BorderLayout.NORTH);

    // end Variant 1

// All remaining code unchanged from section 4.4

DialogLayout.java

see \Chapter4\4\dl

package dl;

import java.awt.*;

import java.util.*;

public class DialogLayout implements LayoutManager

{

  protected int m_divider = -1;

  protected int m_hGap = 10;

  protected int m_vGap = 5;

  public DialogLayout() {}

  public DialogLayout(int hGap, int vGap) {

    m_hGap = hGap;

    m_vGap = vGap;

  }

  public void addLayoutComponent(String name, Component comp) {}

  public void removeLayoutComponent(Component comp) {}

  public Dimension preferredLayoutSize(Container parent) {

    int divider = getDivider(parent);

    int w = 0;

    int h = 0;

    for (int k=1 ; k<parent.getComponentCount(); k+=2) {

      Component comp = parent.getComponent(k);

      Dimension d = comp.getPreferredSize();

      w = Math.max(w, d.width);

      h += d.height + m_vGap;

    }

    h -= m_vGap;

    Insets insets = parent.getInsets();

    return new Dimension(divider+w+insets.left+insets.right,

      h+insets.top+insets.bottom);

  }

  public Dimension minimumLayoutSize(Container parent) {

    return preferredLayoutSize(parent);

  }

  public void layoutContainer(Container parent) {

    int divider = getDivider(parent);

    Insets insets = parent.getInsets();

    int w = parent.getWidth() - insets.left

      - insets.right - divider;

    int x = insets.left;

    int y = insets.top;

    for (int k=1 ; k<parent.getComponentCount(); k+=2) {

      Component comp1 = parent.getComponent(k-1);

      Component comp2 = parent.getComponent(k);

      Dimension d = comp2.getPreferredSize();

      comp1.setBounds(x, y, divider-m_hGap, d.height);

      comp2.setBounds(x+divider, y, w, d.height);

      y += d.height + m_vGap;

    }

  }

  public int getHGap() { return m_hGap; }

  public int getVGap() { return m_vGap; }

  public void setDivider(int divider) {

    if (divider > 0)

      m_divider = divider;

  }

  public int getDivider() { return m_divider; }

  protected int getDivider(Container parent) {

    if (m_divider > 0)

      return m_divider;

    int divider = 0;

    for (int k=0 ; k<parent.getComponentCount(); k+=2) {

      Component comp = parent.getComponent(k);

      Dimension d = comp.getPreferredSize();

      divider = Math.max(divider, d.width);

    }

    divider += m_hGap;

    return divider;

  }

  public String toString() {

    return getClass().getName() + "[hgap=" + m_hGap + ",vgap="

      + m_vGap + ",divider=" + m_divider + "]";

  }

}

Understanding the Code

Class FlightReservation

This class now imports package dl and sets that layout for JPanel p1r (which contains the labels and input fields). Package dl contains our custom layout, DialogLayout.

Class DialogLayout

This class implements the LayoutManager interface to serve as our custom layout manager. Three instance variables are needed:

int m_divider: width of the left components. This can be calculated or set to some mandatory value.

int m_hGap: horizontal gap between components.

int m_vGap: vertical gap between components.

Two constructors are available to create a DialogLayout: a no-argument default constructor and a constructor which takes horizontal and vertical gap sizes as parameters. The rest of the code implements methods from the LayoutManager interface.

Methods addLayoutComponent() and removeLayoutComponent() are not used in this class and receive empty implementations. We do not support an internal collection of the components to be managed. Rather, we refer to these component directly from the container which is being managed.

The purpose of the preferredLayoutSize() method is to return the preferable container size required to lay out the components in the given container according to the rules used in this layout. In our implementation we first determine the divider size (the width of the first column plus the horizontal gap, m_hGap) by calling the getDivider() custom method.

        int divider = getDivider(parent);

If no positive divider size has been specified using the setDivider() method (see below), the getDivider() method looks at each even indexed component in the container (this should be all the labels if the components were added to the container in the correct order) and returns the largest preferred width found plus the horizontal gap value, m_hGap (which defaults to 10 if the default constructor is used):

    if (m_divider > 0)

      return m_divider;

    int divider = 0;

    for (int k=0 ; k<parent.getComponentCount(); k+=2) {

      Component comp = parent.getComponent(k);

      Dimension d = comp.getPreferredSize();

      divider = Math.max(divider, d.width);

    }

    divider += m_hGap;

    return divider;

Now, back to the preferredLayoutSize() method. Once getDivider returns we then examine all components in the container with odd indices (this should be all the input fields) and determine the maximum width, w. This is found by checking the preferred width of each input field. While we are determining this maximum width, we are also continuing to accumulate the height, h, of the whole input fields column by summing each field's preferred height (not forgetting to add the vertical gap size, m_vGap, each time; notice that m_vGap is subtracted from the height at the end because there is no vertical gap for the last field). (Remember that m_vGap defaults to 5 if the the default constructor is used.)

    int w = 0;

    int h = 0;

    for (int k=1 ; k<parent.getComponentCount(); k+=2) {

      Component comp = parent.getComponent(k);

      Dimension d = comp.getPreferredSize();

      w = Math.max(w, d.width);

      h += d.height + m_vGap;

    }

    h -= m_vGap;

So at this point we have determined the width of the labels column (including the space between columns), divider, and the preferred hieght, h, and width, w, of the input fields column. So divider+w gives us the preferred width of the container, and h gives us the total preferred height. Not forgetting to take into account any Insets that might have been applied to the container, we can now return the correct preferred size:

    Insets insets = parent.getInsets();

    return new Dimension(divider+w+insets.left+insets.right,

      h+insets.top+insets.bottom);

The purpose of the minimumLayoutSize() method is to return the minimum size required to lay out the components in the given container according to the rules used in this layout. We return preferredLayoutSize() in this method, because we chose not to make a distinction between minimum and preferable sizes (to avoid over-complication).

layoutContainer() is the most important method in any layout manager. This method is responsible for actually assigning the bounds (position and size) for the components in the container being managed. First it determines the size of the divider (as discussed above), which represents the width of the labels column plus an additional m_hGap. From this it determines the width, w, of the fields column by subtracting the container's left and right insets and divider from the width of the whole container:

    int divider = getDivider(parent);

    Insets insets = parent.getInsets();

    int w = parent.getWidth() - insets.left

      - insets.right - divider;

    int x = insets.left;

    int y = insets.top;

Now all pairs of components are examined in turn. Each left component receives a width equal to divider-m_hGap, and all right components receive a width of w. Both left and right components receive the preferred height of the right component (which should be the input field).

Coordinates of the left components are assigned starting with the container's Insets, x and y. Notice that y is continually incremented based on the preferred height of each right component plus the vertical gap, m_vGap. The right components are assigned a y-coordinate identical to their left component counterpart, and an x-coordinate of x+divider (remember that divider includes the horizontal gap, m_hGap):

    for (int k=1 ; k<parent.getComponentCount(); k+=2) {

      Component comp1 = parent.getComponent(k-1);

      Component comp2 = parent.getComponent(k);

      Dimension d = comp2.getPreferredSize();

      comp1.setBounds(x, y, divider-m_hGap, d.height);

      comp2.setBounds(x+divider, y, w, d.height);

      y += d.height + m_vGap;

    }

Method setDivider() allows us to manually set the size of the left column. The int value passed as parameter gets stored in the m_divider instance variable. Whenever m_divider is greater than 0 the calculations of divider size are overridden in the getDivider() method and this value is returned instead.

The toString method provides typical class name and instance variable information. (It is always a good idea to implement informative toString() methods for each class. Although we don't consistently do this throughout this text, we feel that production code should always include this functionality.)

Running the Code

At this point you can compile and execute this example. Figure 4.16 shows the sample interface introduced in the previous section now using DialogLayout to manage the layout of the input fields (text field and two combo boxes) and their corresponding labels. Note that the labels occupy only their preferred space and do not resize when the frame resizes. The gap between labels and boxes can be managed easily by manually setting the divider size with the setDivider() method (discussed above). The input fields form the right column and occupy all remaining space.

Using DialogLayout, all that is required is adding the labels and input fields in the correct order. We can now use this layout manager each time we encounter label/input field pairs without worrying about intermediate containers. In the next section we build upon DialogLayout to create an even more general layout manager that can be used to create complete dialog interfaces very easily.

UI Guideline : Alignment across controls as well as within It is a common mistake in UI Design to achieve good alignment with a control or component but fail to achieve this across a whole screen, panel or dialog. Unfortunately, the architecture of Swing lends itself to this problem. For example, if you have 4 custom components which inherit from a JPanel, each has its own Layout Manager and each is functional in its own right. Then you wish to build a composite component which requires all four. So you create a new Component with a Grid Layout for example, then add each of your 4 components in turn.
The result can be very messy. The fields within each component will align e.g. 3 radio buttons, but those radio buttons will not align with say 3 TextFields in the next component. Why not? The answer is simple. With Swing, there is no way for the layout manager within each component to negotiate with the others. So alignment cannot be achieved across the components. The answer to this problem is that you must flatten out the design into a single panel, as DialogLayout achieves.



[ Return to Swing (Book) ]


Top 10 read Java Articles
 Get free "1000 Java Tips eBook"

 Java Calendar and Date: good to know facts and code examples

 Array vs ArrayList vs LinkedList vs Vector: an excellent overview and examples

 How can I convert any Java Object into byte array? And byte array to file object

 The Java Lesson 1: What is Java?

 How do I compare two dates and times, date between dates, time between times and

 Maven vs Ant or Ant vs Maven?

 How to open, read, write, close file(s) in Java? Examples on move, rename and de

 Java Array

 Java: JLabel font and color


[ More in News Section ]
Java Lessons

The Java Lesson 1:
What is Java?
The Java Lesson 2:
Anatomy of a simple Java program
The Java Lesson 3:
Identifiers and primitive data types
The Java Lesson 4:
Variables, constants, and literals
The Java Lesson 5:
Arithmetic operations, conversions, and casts
The Java Lesson 6:
Boolean expressions and operations
The Java Lesson 7:
Bitwise operations
The Java Lesson 8:
Flow control with if and else
The Java Lesson 9:
switch statements
The Java Lesson 10:
for, while, and do-while statements
The Java Lesson 11:
Using break and continue
The Java Lesson 12:
Class methods and how they are called
The Java Lesson 13:
Using the Math class
The Java Lesson 14:
Creating and calling custom class methods
The Java Lesson 15:
Overloading class methods
The Java Lesson 16:
An introduction to objects and object references
The Java Lesson 17:
The String class
The Java Lesson 18:
The StringBuffer class
The Java Lesson 19:
Initializing and processing arrays of primitives
The Java Lesson 20:
Initializing and processing arrays of objects
The Java Lesson 23:
Inheritance and overriding inherited methods
The Java Lesson 24:
abstract classes and polymorphism
The Java Lesson 25:
Interfaces, instanceof, and object conversion and casting
The Java Lesson 26:
Introduction to graphical programming and the java.awt packa
The Java Lesson 27:
The Component class
The Java Lesson 28:
Containers and simple layout managers
The Java Lesson 29:
The Color and Font classes
The Java Lesson 30:
Drawing geometric shapes
The Java Lesson 31:
Choice, List, and Checkbox controls
The Java Lesson 32:
Using the Scrollbar graphical control
The Java Lesson 33:
Menus and submenus
The Java Lesson 34:
An introduction to applets and the Applet class
The Java Lesson 35:
Essential HTML to launch an applet and pass it parameters
The Java Lesson 36:
Mouse event processing
Java Lesson 37:
Menus and submenus
Java Lesson 38:
The WindowListener interface and the WindowAdapter class
Java Lesson 39:
An introduction to GridBagLayout
Java Lesson 40:
An introduction to the Java Collections API
Java Lesson 41:
Exception handling with try, catch, and finally blocks
Java Lesson 42:
Claiming and throwing exceptions
Java Lesson 43:
Multithreading, the Thread class, and the Runnable interface
Java Lesson 44:
An introduction to I/O and the File and FileDialog classes
Java Lesson 45:
Low-level and high-level stream classes
Java Lesson 46:
Using the RandomAccessFile class
Java Lessons by
Joh Huhtala: Update

Latest articles
 Java Profiler JProbe to Resolve Performance Problems Faster

 SSL with GlassFish v2, page 5

 SSL with GlassFish v2, page 4

 SSL with GlassFish v2, page 3

 SSL with GlassFish v2, page 2

 The Java Lesson 2: Anatomy of a simple Java program, page 2

 New site about Java for robots and robotics: both software and hardware.

 Exceptions -III: What's an exception and why do I care?

 Exceptions -II: What's an exception and why do I care?

 Exceptions: What's an exception and why do I care?

 Double your Java code quality in 10 minutes, here is receipt

 Murach's Java Servlets and JSP

 How to get ascii code from a char in Java?

 Can we just try without catch? Yes!

 Make Tomcat page load faster

 Make your Tomcat More secure - limit network address for certain IP addresses

 New Java book online starts now here...

 Implementing RESTful Web Services in Java

 Firefox trimming from 1 GB to 40 Mb with many tabs opened

 SSL with GlassFish v2

 My request to replublish Tech Tips

 Search JavaFAQ.nu site here

 New Advanced Installer for Java 6.0 brings XML updates and imports 3rd party MSI

 EJB programming restrictions

 Maven vs Ant or Ant vs Maven?

 Why Java does not use default value which it should?

 How to unsign signed bytes in Java - your guide is here

 The Java Lesson 3: Identifiers and primitive data types. Page 2

 The Java Lesson 7: Bitwise operations with good examples, click here! Page 4

 The Java Lesson 7: Bitwise operations with good examples, click here! Page 3


[ More in News Section ]


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