Holiday fun with JavaFX
After seeing all of the hype surrounding JavaFX and it’s recent 1.0 release, I decided to go ahead and play around with it a bit. Here’s a bit of what I learned, and a cool little holiday app you can run.
WTF is JavaFX and why might you care?
JavaFX is basically a way to develop much cooler GUIs with much less effort than Swing and have your app run almost everywhere. I would argue that it’s not as easy as Flex BUT it allows gives you more control.
A little holiday app for you
If you want to try it out yourself, just run it here (with JavaFX installed) or grab the NetBeans JavaFX project, unzip it, and import it. Here is the code:
package holiday;
import javafx.animation.*;
import javafx.scene.*;
import javafx.scene.control.TextBox;
import javafx.scene.image.*;
import javafx.scene.input.MouseEvent;
import javafx.scene.paint.Color;
import javafx.scene.shape.*;
import javafx.scene.text.*;
import javafx.stage.*;
import java.lang.System;
var stageWidth = 325;
var stageHeight = 200;
var titleFont = Font { name:"Helvetica", size: 15 };
var footerFont = Font { name:"Helvetica", size: 10 };
// Node positioning
var titleHeight = 25;
var detailY = stageHeight;
var footerHeight = 25;
class Holiday {
public var title:String;
public var greeting:String;
}
var holidays:Holiday[];
public class HolidayList extends CustomNode {
public var holidays:Holiday[];
public override function create():Node {
return Group {
translateX: 0, translateY: 10,
content: bind for(i in holidays) {
var holidayY = indexof i * 20 + titleHeight;
Group {
id: "{i.title}",
content: [
HolidayListItem {
holiday: i,
holidayY: holidayY,
originalHolidayY: holidayY
}
]
}
}
}
}
}
var holidayList = HolidayList { holidays: bind holidays };
class HolidayListItem extends CustomNode {
public var holiday:Holiday;
var expanded:Boolean = false;
public var holidayY:Number;
public var originalHolidayY:Number;
public var holidayWidth:Number = stageWidth - 20;
public var holidayHeight:Number = 20;
public override function create():Node {
return Group {
translateY: bind holidayY,
blocksMouse: true,
clip: Rectangle {
width: bind holidayWidth,
height: stageHeight,
fill: Color.WHITE
},
content: [
Rectangle {
width: holidayWidth,
height: stageHeight,
fill: Color.WHITE
},
Text {
content: holiday.title,
translateX: 10, translateY: 10,
},
Rectangle {
var holidayHoverFill = Color.rgb(0,0,0,0);
y: -14, x: 10,
width: holidayWidth,
height: bind holidayHeight + 10,
fill: bind holidayHoverFill,
onMouseEntered: function(e:MouseEvent) { holidayHoverFill = Color.rgb(0,0,0,0.07); e.node.cursor = Cursor.HAND; },
onMouseExited: function(e:MouseEvent) { holidayHoverFill = Color.rgb(0,0,0,0); e.node.cursor = Cursor.DEFAULT; },
onMouseClicked: function(e:MouseEvent) {
if (currentHoliday == holiday) {
currentHoliday = null;
} else {
currentHoliday = holiday;
}
System.out.println("--applying onclick for {currentHoliday.title}");
if (expanded == false) {
var expand = Timeline {
keyFrames: [
KeyFrame {
time:0.6s values:holidayY => 20.0 tween Interpolator.LINEAR
action: function() {
holidayHeight = 150;
}
},
]
};
expand.play();
expanded = true;
}
else {
var collapse = Timeline {
keyFrames: [
KeyFrame {
time:0.6s values:holidayY => originalHolidayY tween Interpolator.LINEAR
action: function() {
holidayHeight = 20;
}
},
]
};
collapse.play();
expanded = false;
}
}
}
]
}
}
}
class HolidayDetail extends CustomNode {
public var curHoliday:Holiday = Holiday {
title: "NA",
greeting: "NA"
}
public var detailFillColor:Color = Color.RED;
public var detailFont:Font = Font.font("sansserif", FontWeight.REGULAR,24);
public var detailBgColor:Color = Color.WHITE;
public override function create():Node {
System.out.println("----got holiday {curHoliday.title}");
return Group {
content: [
Rectangle {
width: stageWidth,
height: stageHeight-60,
fill: bind detailBgColor,
blocksMouse: true
},
Text { content: bind curHoliday.greeting, translateX: 10, translateY: 40, font: detailFont, fill: detailFillColor },
]
}
}
}
var holidayDetailPane = HolidayDetail { translateY: bind detailY };
// Shows and hides Holiday detail when a Holiday is clicked
public var currentHoliday:Holiday = null on replace {
var newDetailY = stageHeight; //Default to below the stage
if (currentHoliday != null) {
newDetailY = 27 + titleHeight;
holidayDetailPane.curHoliday = currentHoliday;
System.out.println("clicked holiday: {currentHoliday.title}");
}
// Show/hide detail pane
var anim = Timeline {
keyFrames: [
KeyFrame { time: 0s, values: detailY => detailY tween Interpolator.LINEAR },
KeyFrame { time: 0.7s, values: detailY => newDetailY tween Interpolator.LINEAR }
]
};
anim.play();
}
var titleBar = Group {
content: [
Rectangle { width: stageWidth, height: titleHeight, fill: Color.WHITE },
Text { x: 85, y: 18, font: titleFont, fill: Color.BLACK content: "Choose-a-holiday" },
Line { startX: 0, startY: titleHeight, endX: stageWidth, endY: titleHeight, stroke: Color.DARKGRAY }
]
}
var footerBar = Group {
content: [
Rectangle { x: 0, y: stageHeight-footerHeight, width: stageWidth, height: footerHeight, fill: Color.WHITE },
Text { x: 75, y: stageHeight - 10, font: footerFont, fill: Color.BLACK, content: "created by Eric Wendelin" }
]
}
def stage = Stage {
title: "Choose-a-holiday",
resizable: true,
width: stageWidth,
height: stageHeight,
visible: true,
scene: Scene {
content: Group {
content: bind [
holidayList,
holidayDetailPane,
titleBar,
footerBar
]
clip: Rectangle {
width: stageWidth,
height: stageHeight
}
}
fill: Color.TRANSPARENT
}
}
function run() {
//Add holidays
insert Holiday {
title: "Christmas",
greeting: "Merry Christmas!"
} into holidays;
insert Holiday {
title: "Hanukkah",
greeting: "Happy Hanukkah!"
} into holidays;
insert Holiday {
title: "Kwanzaa",
greeting: "Happy Kwanzaa!"
} into holidays;
insert Holiday {
title: "Festivus",
greeting: "Festivus for the rest of us!"
} into holidays;
insert Holiday {
title: "New Year's Day",
greeting: "Happy New Year!"
} into holidays;
}
Cool, no?
How to make your own JavaFX app
I downloaded the NetBeans with JavaFX bundle since that seemed like the easiest way to get started. They say it doesn’t work on Linux, but they lie. Follow these instructions and you’ll be setup quickly.
Create a new "JavaFX Script Application" project, set it as the main project, and have it auto-create a Main .fx file. Copy the source above and hit "Run Main Project". If you decide to play around with JavaFX, you’ll definitely need the JavaFX API Docs.
Enjoy! Merry Christmas!
Wow, I’m so impressed :) Seriously, thank you for taking the time to show this to us. Happy Holidays to you too!
Thanks Chris :)
Very nice job. I have to try to get JavaFX working with Linux…
Merry Christmas!
I appreciate the work you put into this. But seriously, look at the amount of code you had to write to get this little example going. Sun can’t be serious. If I write a “classic” Java applet it’s still less code, with anything else it’s a lot less. This is the first example of JavaFX I looked at and it really is off-putting.
@Stephan:
I understand your position there. I probably *could* have kept it to about 100 lines, but I over-engineered it to be flexible. I like the amount of control I have for the UI, but it did take a lot of (perhaps unnecessary) code.
I may have other (simpler?) examples of JavaFX in the future. Don’t count it out just yet.
[...] A small application which demonstrates how to create a nice menu where the items fade in / out. [...]