Collin Fagan is a Sr. Software Engineer at NAVTEQ working on next generation map building software. He holds a BS in Computer Science from the Rochester Institute of Technology. Collin has worked in the Map, Medical, Produce Warehousing and Telecommunications industries on a wide assortment of projects ranging from customized Linux thin clients to highly concurrent telecommunications control systems. Collin has posted 13 posts at DZone. View Full User Profile

Why Does JavaFX Get to Cheat?

03.25.2008
| 11117 views |
  • submit to reddit

Why does JavaFX get to do things that would be frowned upon if done in plain old Java? The prime example is it's treatment of layout managers and panels. In JavaFX a layout manager is bound together with a JPanel to create one cohesive unit. The API is then tailored specifically around the needs of that combonation of layout manager and panel. Now I look at the code and say to myself, "Hey that's convenient", and it IS, but that's not that point. Someone at some point in the existence of Java decided that the layout logic should be separate from the storage of components in a container. I'm sure there are good reasons for this separation. Now JavaFX seems to be making the statement that the separation was not a good idea. In essence saying that an easy to use API trumps this particular design pattern and justifies tight coupling. I can't say I disagree in this circumstance. I just want to lever the playing field. If JavaFX gets to break the rules so can I.

Panel Classes.

Throwing caution to the wind I used every dirty trick I know to try and create some easy to use panels for plain old Java. Here is what I have so far.

  • BorderPanel
  • CardPanel
  • GridPanel
  • BoxPanel
  • GBFPanel (Grid Bag Factory Panel)

BorderPanel, CardPanel and GridPanel

The border and card panels just add some convenient methods methods and are not that exciting. I have methods like setNorth() for BorderPanel and next() and previous() for CardPanel. With GridPanel I decided to create a constructor that takes a two dimensional array of components. In this way you can specify the exact location of every component in the grid in a way that actually reflects the layout of the final product. GridPanel Example:

Component[][] grid = new Component[][] {
{ new JButton("1"), new JButton("A") },
{ GridPanel.spacer(), new JButton("B"), new JButton("Blue") },
{ new JButton("3") } };
GridPanel gp = new GridPanel(grid, 5, 5);

 

As an added bonus this constructor will automatically add empty space when a row is under sized.

BoxPanel

With BoxPanel I was able to get a litte more creative. I added an enum called DIRECTION which defines spacer(), glue() and build() methods. I also added a varags version of add so that multiple components can be added in the same line. Box Example 1: - This code shows the new addSpacer() method, and the varargs version of add.

BoxPanel bp = new BoxPanel(X_AXIS);

bp.addSpacer(5);
bp.add(new JLabel("Section 1"), X_AXIS.spacer(5), new JButton("A"), X_AXIS.spacer(5), new JButton("B"));
bp.addSpacer(10);
bp.add(new JComboBox());
bp.addGlue();
bp.add(new JLabel("Section 2"), X_AXIS.spacer(5), new JButton("1"), X_AXIS.spacer(5), new JButton("2"));
bp.addSpacer(5);

Box Example 2: - This code shows the new build() method of Direction. It also nests BoxPanels to make a grid.

BoxPanel rowsPanel = new BoxPanel(Y_AXIS);
rowsPanel.add(Y_AXIS.spacer(10));
for(int rows = 0; rows < 10; rows++ ){
if(rows % 2 == 0){
rowsPanel.add(X_AXIS.build(X_AXIS.spacer(5), new JLabel("Row :" + rows), X_AXIS.spacer(5) , new JComboBox(), X_AXIS.spacer(5)));
}else{
BoxPanel oddColumns = new BoxPanel(X_AXIS);
oddColumns.add(X_AXIS.spacer(5), new JLabel("Row :" + rows), X_AXIS.spacer(5) ,new JButton("Button 1"), X_AXIS.glue());
oddColumns.addSpacer(10);
oddColumns.add(new JTextField(), X_AXIS.spacer(5) ,new JButton("Button 2"), X_AXIS.glue(), X_AXIS.spacer(5));
rowsPanel.add(oddColumns);
}
rowsPanel.add(Y_AXIS.spacer(10));
}

GBFPanel - GridBagFactoryPanel

GridBagLayout is by far one of the most complicated layout managers in the JDK. I decided that most of the problems come from the GridBagConstraints object. It just seems that the add(Component comp, Object constraint) method of Container is just not flexible enough to handle the kinds of constraints GridBag really needs. I wanted to deal with only the individual constraints that are relevant to the component I'm dealing with. Here is what I came up with.

public class DemoPanel extends GBFPanel {

private JButton button1 = new JButton("Button 1");
private JButton button2 = new JButton("Button 2");
private JButton button3 = new JButton("Button 3");
private JButton button4 = new JButton("Button 4");
private JButton button5 = new JButton("Button 5");
private JButton button6 = new JButton("Button 6");
private JButton button7 = new JButton("Button 7");
private JButton button8 = new JButton("Button 8");
private JButton button9 = new JButton("Button 9");
private JButton button10 = new JButton("Button 10");

public DemoPanel(){

addDefaultConstraints(FILL.BOTH);

add(button1, WEIGHT.x(1));
add(button2, WEIGHT.x(1));
add(button3, WEIGHT.x(1));
add(button4, GRID.WIDTH.REMAINDER);

add(button5, GRID.WIDTH.REMAINDER);
add(button6, GRID.WIDTH.RELATIVE);
add(button7, GRID.WIDTH.REMAINDER);
add(button8, GRID.HEIGHT.REMAINDER, WEIGHT.y(1));

add(button9, GRID.WIDTH.REMAINDER);
add(button10, GRID.WIDTH.REMAINDER);

}
}

Recognize this code? No? It's the GridBagLayout javadoc example.

See? And in just 10 lines of layout code, with no side effects to keep track of. Ok let me explain what's going on. I started by creating an interface defining the job of a single constraint.

public interface GridBagFactoryConstraint {

public void apply(GridBagConstraints gbc);

}
Then using a combination of enums and static factory methods I implemented a class representing every constraint from GridBagConstraints. Here is the implementation of the FILL enum as an example.
public enum FILL implements GridBagFactoryConstraint {

HORIZONTAL(GridBagConstraints.HORIZONTAL),
VERTICAL(GridBagConstraints.VERTICAL),
BOTH(GridBagConstraints.BOTH),
NONE(GridBagConstraints.NONE);

private int gbcValue;

private FILL(int val) {
gbcValue = val;
}

public void apply(GridBagConstraints gbc) {
gbc.fill = gbcValue;
}
}
I know what you are thinking and yes, I really did all of them. The next step was to create an add method in GBFPanel that would accept a variable number of these constraint objects.
public void add(Component comp, GridBagFactoryConstraint... constraints) {
....
}
This is the add method used in my example code above. Now some of you may have noticed the call to addDefaultConstraints(). This method lets you specify constraints that you wish to be applied to every component added to the panel. Its handy for factoring out common constraints. The constraints passed in the add method will always override any default values. Some time ago there was a "layout manager shoot-out" over on java.net. Here is how a GBFPanel entry might have looked.
public class AddressShootout extends GBFPanel {

private JTextField lastNameTF = new JTextField(15);
private JTextField firstNameTF = new JTextField(15);
private JTextField phoneTF = new JTextField(15);
private JTextField addressTF = new JTextField(20);
private JTextField emailTF = new JTextField(15);
private JTextField stateTF = new JTextField(2);
private JTextField cityTF = new JTextField(15);

public AddressShootout() {

addDefaultConstraints(ANCHOR.E, INSETS.top(5), INSETS.left(5), INSETS.right(5));

add(new JLabel("Last Name"));
add(lastNameTF, FILL.HORIZONTAL, WEIGHT.x(1));
add(new JLabel("First Name"));
add(firstNameTF, FILL.HORIZONTAL, WEIGHT.x(1), GRID.WIDTH.REMAINDER);

add(new JLabel("Phone"));
add(phoneTF, FILL.HORIZONTAL, WEIGHT.x(1));
add(new JLabel("Email"));
add(emailTF, FILL.HORIZONTAL, WEIGHT.x(1), GRID.WIDTH.REMAINDER);

add(new JLabel("Address"));
add(addressTF, FILL.HORIZONTAL, WEIGHT.x(1), GRID.WIDTH.REMAINDER);

add(new JLabel("City"), INSETS.bottom(5));
add(cityTF, FILL.HORIZONTAL, WEIGHT.x(1), INSETS.bottom(5));
add(new JLabel("State"), INSETS.bottom(5));
add(stateTF, FILL.HORIZONTAL, WEIGHT.x(1), INSETS.bottom(5));

}
}

I reimplemented both the javadoc and Java tutorial examples for GridBagLayout and discovered two things.

1. This gets the job done in a lot less code.

2. The examples are almost totally useless.

I then went on to reimplement a number of other "example layouts" from some of the 3rd party layout managers. I again discovered two things.

1. This gets the job done in close to the same amount of code.

2. Their examples are much more focused on actual development situations.

Conclusions

I think GridBag benefits the most from this evil tight coupling. The other layouts became easier to read but gained little real power. It's safe to say that the more complicated a layout manger the more it will benefit from tight coupling. Other possibilities for this treatment include SpringPanel, OverlayPanel, FlowPanel, GroupPanel, TablePanel, MigPanel, FormPanel and PagePanel. So this is evil, right? JavaFX gets to do it. Why shouldn't we? Is this an architectural compromise that's worth it?

Published at DZone with permission of its author, Collin Fagan.

(Note: Opinions expressed in this article and its replies are the opinions of their respective authors and not those of DZone, Inc.)

Comments

Carl Antaki replied on Wed, 2008/03/26 - 10:17am

JGoodies FormLayout should be included in JavaFX and JavaFX should also support building GUIs using XML.

JavaFX is lagging behind other technologies such as Flex and Silverlight. I can't really see any benefits apart from the Java compatibility. I find the syntax to be ugly. I find writing Java code to be much more pleasurable.

 

Carl

Mike P(Okidoky) replied on Wed, 2008/03/26 - 11:25am

Maybe at one point the rationale was that you at the point where you plunk down all the buttons, you don't concern yourself with the layout.  Because that wasn't possible, it let you add a single constraint variable.  With the layout managers we've seen, the constraint ties it to the layout.  eg. BorderLayout.WEST.  Only has meaning when using the BorderLayout.

But what if you use names for the constraints, and have a layout construction mode inside your application that you can enable at runtime, which will let you position all the components.  It could choose different layouts for different languages.  The layout can be configured and changed without changing any code.  That layout then persists to disk, which you can then include in the distribution, so that it becomes the default.  This 'construction mode' might only be enabled using a feature that only developers would enable.  This would enable graphic designers to help design application layouts.  The construction mode should also allow for imagery.  If things were done this way from the beginning, Java applications would have looked way more sexy I think.
So this would be an example of separating design from program.  Current design strategies, both thick and thin client, have left graphic designers on the side lines.  Programmers think they can design things graphically.  They almost never can.  The result is crappy designs all over the internet, and in applications, since day one.  Flex like GUI applications tops them all, absolute chaos.

Anyway, since we haven't had any separation, really, your panels are improvements I think. 

 

Filipe Sabella replied on Wed, 2008/03/26 - 1:09pm

I truly don't understand. Using JGoodies Forms, with grid or absolute layout, doing any of the examples if a matter of minutes - without drag'n'drop or cappy generated code, but writing the code yourself.

Collin Fagan replied on Wed, 2008/03/26 - 1:29pm

@Mike P

I'm actually surprised that someone has not already created a runtime based layout manager. I'm sure it's very possible. Thanks for the positive feedback.

Collin

Mikael Grev replied on Wed, 2008/03/26 - 1:35pm

FormLayout would be better for sure, but MiG Layout would be even better, IMO. It has everything FormLayout has and a lot more.

The last think one should do is trying to shoe horn GridBagLayout into anything.

Why not vote at the RFE(s) for MigLayout:

JDK/Swing RFE: http://bugs.sun.com/bugdatabase/view_bug.do?bug_id=6530906 (it is on the top 25 REF list)

SWT/Nebula RFE:  https://bugs.eclipse.org/bugs/show_bug.cgi?id=214698  (the most voted for R

There will also be a Technical Session on MiG Layout at JavaOne.

Cheers,
Mikael Grev (Author of MiG Layout)

Collin Fagan replied on Wed, 2008/03/26 - 1:41pm in response to: Mikael Grev

So Mikael what do you think about a MigPanel? Would a specialized API for interacting with MigLayout simplify anything?

Mikael Grev replied on Wed, 2008/03/26 - 4:08pm in response to: Collin Fagan

Hello Collin,

Yes, it probably would. Especially since that seems to be the way to do it in JavaFX (if I read you correctly). I have made it a point to have all the constraints of MigLayout in as a component constraint (during add) or as a constraint to the MigLayout constructor, so it should be quite easy to do.

Some things would be nice to have directly on the panel, such as enabling the debug mode. migPanel.setDebug(boolean).

Cheers,
Mikael

Richard Kennard replied on Wed, 2008/03/26 - 6:06pm in response to: Collin Fagan

Collin,

FYI: I think Metawidget does what you want. You drop JComponents into a JPanel and it lays them out for you at runtime using the standard Java LayoutManager of your choice.

Going one better, Metawidget can even choose the JComponents for you, based on inspecting an underlying business object.

If you get chance to give it a try, I'd appreciate any feedback.

Regards,

Richard.

ff aaa replied on Wed, 2008/03/26 - 8:51pm

i thought Java FX was primarily a solution for Java 2D, not for Swing.

Andres Almiray replied on Thu, 2008/03/27 - 11:47am in response to: ff aaa

Actually JavaFx Script is more than an abstraction layer over Java2D, but many of the examples shown so far strenghten your perception that is only usable for drawing. You may find a more compelling example of what JavaFX can do when mixing Swing and Java2D at http://www.canoo.com/blog/2007/06/15/javafx-script-canoo-music-pinboard/

Brian Young replied on Thu, 2008/03/27 - 2:55pm

This is an honest question- who is using JavaFX and who do with think will be using it in the future?  A monster.com search on "JavaFX" reveals 4 hits, one of which is at Sun.  We all know the place of J2EE, Java applets, Java applications, and WebStart.  I see many stories on JavaFX but I'm not really sure why/where/when I would pick it over another more established Java technology. 

 Are you using JavaFX?  If so, is it for a consumer based app, enterprise app, or other?

Andres Almiray replied on Thu, 2008/03/27 - 4:25pm

Brian, due to the nature of available material and tool support, it is my belief that JavaFx has not make it out of the incubator (I'm really hoping to be suprprised in a month when JavaOne rolls out) but I wouldn't jump to JavaFX just yet for my next project, as the API is still in flux (the language is moving from interpreted to compiled mode).

Mike P(Okidoky) replied on Thu, 2008/03/27 - 8:50pm

Can't this whole scripting vs compiling thing become transparent?  Is it possible to just have it compile behind the scenes.  Just throw a script at it, and compile it before running it.  No need to impose any build script at the developer.  The compiled code could be cached as well.  Keep it feeling like a dynamicly changeable script, yet get compilation runtime speeds.

Andres Almiray replied on Fri, 2008/03/28 - 12:08am

You mean like Groovy does? ;-)

Comment viewing options

Select your preferred way to display the comments and click "Save settings" to activate your changes.