With the new, improved, custom layout capabilities in JavaFX 1.3, I thought it'd be useful to post a simple example. This example defines a custom layout in JavaFX whose behavior is like the BorderLayout, familiar to most Java programmers. Here's a screen shot:
There are actually two ways to create custom layouts in JavaFX:
- Sub-class javafx.scene.layout.Container
- Create a javafx.scene.layout.Panel instance
Here is the code from today's example that employs the first way. Note that I'm leveraging the Node#id instance variable to supply the directional values needed by the traditional BorderLayout.
/*
* BorderLayout.fx -
* Example of subclassing Container to create a custom layout
*/
package containersubclasslayoutexample.layout;
import javafx.scene.layout.*;
import javafx.scene.layout.Container.*;
public class BorderLayout extends Container {
var topHeight:Number;
var bottomHeight:Number;
var leftWidth:Number;
var rightWidth:Number;
override function doLayout():Void {
for (node in getManaged(content)) {
if (node.id == "top") {
topHeight = getNodePrefHeight(node);
setNodeWidth(node, width);
setNodeHeight(node, getNodePrefHeight(node));
positionNode(node, 0, 0);
}
else if (node.id == "bottom") {
bottomHeight = getNodePrefHeight(node);
setNodeWidth(node, width);
setNodeHeight(node, getNodePrefHeight(node));
positionNode(node, 0, height - bottomHeight);
}
else if (node.id == "left") {
leftWidth = getNodePrefWidth(node);
setNodeWidth(node, getNodePrefWidth(node));
setNodeHeight(node, height - (topHeight + bottomHeight));
positionNode(node, 0, topHeight);
}
else if (node.id == "right") {
rightWidth = getNodePrefWidth(node);
setNodeWidth(node, getNodePrefWidth(node));
setNodeHeight(node, height - (topHeight + bottomHeight));
positionNode(node, width - rightWidth, topHeight);
}
else if (node.id == "center") {
setNodeWidth(node, width - (leftWidth + rightWidth));
setNodeHeight(node, height - (topHeight + bottomHeight));
positionNode(node, leftWidth, topHeight);
}
}
}
override function getPrefWidth(height:Number):Number {
return width;
}
override function getPrefHeight(width:Number):Number {
return height;
}
}
Here is a code example of using the BorderLayout shown above.
/*
* ContainerSubclassLayoutExample.fx -
* Example of subclassing Container to create a custom layout
*/
package containersubclasslayoutexample;
import javafx.stage.Stage;
import javafx.scene.Scene;
import javafx.scene.control.*;
import containersubclasslayoutexample.layout.BorderLayout;
var nodes = [
Button {
text: "Top"
id: "top"
},
Button {
text: "Bottom"
id: "bottom"
},
Button {
text: "Left"
id: "left"
},
Button {
text: "Right"
id: "right"
},
Button {
text: "Center"
id: "center"
}
];
Stage {
var sceneRef:Scene;
title: "Container Subclass Layout Example"
scene: sceneRef = Scene {
width: 500
height: 300
content: [
BorderLayout {
width: bind sceneRef.width
height: bind sceneRef.height
content: nodes
}
]
}
}
Lastly, here is an example of defining the BorderLayout custom layout functionality using the second way listed above, which is to create a Panel instance and override its behavior:
/*
* PanelLayoutExample.fx -
* Example of using a Panel to create a custom layout
*/
package panellayoutexample;
import javafx.stage.Stage;
import javafx.scene.Scene;
import javafx.scene.control.*;
import javafx.scene.layout.*;
import javafx.scene.layout.Container.*;
var nodes = [
Button {
text: "Center"
id: "center"
},
Button {
text: "Top"
id: "top"
},
Button {
text: "Bottom"
id: "bottom"
},
Button {
text: "Left"
id: "left"
},
Button {
text: "Right"
id: "right"
},
];
Stage {
var panel:Panel;
var sceneRef:Scene;
title: "Panel Layout Example"
scene: sceneRef = Scene {
width: 500
height: 300
content: [
panel = Panel {
var topHeight:Number;
var bottomHeight:Number;
var leftWidth:Number;
var rightWidth:Number;
content: bind nodes
width: bind sceneRef.width
height: bind sceneRef.height
onLayout: function():Void {
for (node in getManaged(panel.content)) {
if (node.id == "top") {
topHeight = getNodePrefHeight(node);
setNodeWidth(node, panel.width);
setNodeHeight(node, getNodePrefHeight(node));
positionNode(node, 0, 0);
}
else if (node.id == "bottom") {
bottomHeight = getNodePrefHeight(node);
setNodeWidth(node, panel.width);
setNodeHeight(node, getNodePrefHeight(node));
positionNode(node, 0, panel.height - bottomHeight);
}
else if (node.id == "left") {
leftWidth = getNodePrefWidth(node);
setNodeWidth(node, getNodePrefWidth(node));
setNodeHeight(node, panel.height - (topHeight + bottomHeight));
positionNode(node, 0, topHeight);
}
else if (node.id == "right") {
rightWidth = getNodePrefWidth(node);
setNodeWidth(node, getNodePrefWidth(node));
setNodeHeight(node, panel.height - (topHeight + bottomHeight));
positionNode(node, panel.width - rightWidth, topHeight);
}
else if (node.id == "center") {
setNodeWidth(node, panel.width - (leftWidth + rightWidth));
setNodeHeight(node, panel.height - (topHeight + bottomHeight));
positionNode(node, leftWidth, topHeight);
}
}
}
prefWidth: function(height:Number):Number {
return panel.width;
}
prefHeight: function(width:Number):Number {
return panel.height;
}
}
]
}
}
To learn more about using and creating layouts in JavaFX 1.3, see the blog posts that Amy Fowler has been writing on the subject, and the Custom Layouts chapter in the upcoming new edition of the Clarke/Bruno/Connors JavaFX book.
A note regarding the JavaFX RIA Exemplar Challenge: We have received several entries and I'm in the process of packaging them up and sharing them with the judges. The winner will be announced in early June, 2010.
Regards,
Jim Weaver
Recent Comments