Placing components on each other

Two weeks ago, I was presenting my course Design Patterns - The Timeless Way of Coding to some experienced Java developers, and we spent quite a bit of time arguing about the Composite pattern. To refresh your memory, the book on OO Design Patterns by Erich Gamma et al, contains the following classes in the structure section on Composite:

public abstract class Component {
  public void add(Component c) {}
  public void remove(Component c) {}
  public abstract void operation();
}
public class Leaf extends Component {
  public void operation() { /* do something */ }
}
public class Composite {
  private java.util.List children = new java.util.LinkedList();
  public void add(Component c) { children.add(c); }
  public void remove(Component c) { children.remove(c); }
  public void operation() {
    // or my cool VisitingIterator from last week ;-)
    java.util.Iterator it = children.iterator();
    while(it.hasNext()) {
      ((Component)it.next()).operation();
    }
  }
}
The question we were kicking around was: "Why are the add() and remove() methods in the top-level Component class?"
For an excellent discussion around this question, have a look at the Pattern Hatching column by John Vlissides in the September 2001 edition of Java Report.
Hoping to have whet your appetite, I will not pursue that discussion further here, but instead jump over to the JDK. A few years ago, I was looking at the Composite pattern and comparing it to the java.awt.Component. I found it interesting that java.awt.Component did not contain the add() and remove() methods, those were contained in the subclass java.awt.Container. However, when I looked at javax.swing.JComponent I noticed that it extended java.awt.Container and therefore contained the methods add() and remove().
The big question is: Why? Was it done like this because Java does not have multiple inheritance or was it done to more closely follow the Composite pattern? My bet is on multiple inheritance being the reason, but if you have reliable information (not speculation) I'd love to hear from you.
So, how do you put this to use?

Multi-lined button

Say for example you want to have a JButton with multiple lines of labels on it. One way is to use HTML text, but then the default font is different to the normal JButton font. An answer is to stick a few JLabels on top of a JButton (remember that JButton extends AbstractButton extends JComponent extends Container). You can make such a button by doing the following:
import javax.swing.*;
import java.awt.*;

public class MultilineButton {
  public static void main(String[] args) {
    JButton button = new JButton();
    // It is important to set the layout manager of the button
    button.setLayout(new GridLayout(0, 1));
    // Then we simply add components to it!
    button.add(new JLabel("This is a", JLabel.CENTER));
    button.add(new JLabel("multiline", JLabel.CENTER));
    button.add(new JLabel("button.", JLabel.CENTER));
    JFrame f = new JFrame("Multi-line Button");
    f.getContentPane().setLayout(new FlowLayout());
    f.getContentPane().add(button);
    f.setSize(100,100);
    f.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
    f.show();
  }
}
This will produce a button with several lines on it. The problem with that example is that the focus of the button is not shown.

CheckBox on a Button

What we can also do is put a JCheckBox (or any Component for that matter) on top of a JButton. This can be used to make really confusing user interfaces. Please don't actually do this, I'm just illustrating something here...
import javax.swing.*;
import java.awt.*;

public class CheckBoxOnButton {
  public static void main(String[] args) {
    JButton records = new JButton();
    records.setLayout(new BorderLayout());
    records.add(new JLabel("Show Records"), BorderLayout.NORTH);
    records.add(new JCheckBox("autoscroll"), BorderLayout.CENTER);
    JFrame f = new JFrame("CheckBox on Button");
    f.getContentPane().setLayout(new FlowLayout());
    f.getContentPane().add(records);
    f.setSize(200,200);
    f.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
    f.show();
  }
}
Not very useful? You've seen nothing yet!

Why not a Tree on a Button?

Of course, if we can add a JCheckBox to a JButton, why can we not add a JTree to a JButton? Here's an example of how you can do that:
import javax.swing.*;
import javax.swing.event.*;
import java.awt.*;

public class TreeOnButton {
  public static void main(String[] args) {
    JButton button = new JButton();
    button.setLayout(new BorderLayout());

    final JLabel buttonText = new JLabel("Press me",
      JLabel.CENTER);
    button.add(buttonText, BorderLayout.NORTH);

    JTree tree = new JTree();
    tree.addTreeSelectionListener(new TreeSelectionListener() {
      public void valueChanged(TreeSelectionEvent e) {
        buttonText.setText("Press for " +
          e.getPath().getLastPathComponent());
      }
    });
    button.add(tree, BorderLayout.CENTER);

    JFrame f = new JFrame("Tree on Button");
    f.getContentPane().setLayout(new FlowLayout());
    f.getContentPane().add(button);
    f.setSize(500,500);
    f.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
    f.show();
  }
}
Well, there you go. I have not found any IDE's which support this functionality directly, and I would be surprised if there was such an IDE. You should not really add complex components to each other, as it will confuse your users. Imagine trying to write a user manual for buttons containing trees and check boxes!
This was again one of the funnest newsletters to write, I'm looking forward to your feedback :-)

created by Dr. Heinz M. Kabutz

0 komentar:

Posting Komentar

Twitter Delicious Facebook Digg Stumbleupon Favorites More

 
This Theme Modified by Kapten Andre based on Structure Theme from MIT-style License by Jason J. Jaeger