Tray component in JavaFX
March 17, 2009
To aid my exploration of JavaFX I created the following tray-type component (as seen in OS X to display Dashboard widgets). When the disclosure button is pressed, the main content area slides up to reveal a tray area where controls or other information can be placed.
The code is straight-forward, containing a DisclosureButton class (the black cross button) and the main script, which composes the tray, main content area and button. I had to inline quite a bit, as many of the components need to reference other components for visual calculations – I’m still learning what the right balance of declarative syntax is.
I’m not particularly happy with the animation performance on OS X (you’ll notice it’s a bit choppy). Commenting out the main content areas drop shadow eliminates this issue – maybe future Java updates on the Mac will address this problem.
Here are the two states of the window:
And here’s the code (note that I’m not crazy about the way NetBeans formats the code, but I’ve given up fighting with it):
Main.fx
import javafx.scene.effect.DropShadow; import javafx.scene.Group; import javafx.scene.paint.Color; import javafx.scene.paint.LinearGradient; import javafx.scene.paint.Stop; import javafx.scene.Scene; import javafx.scene.shape.Rectangle; import javafx.scene.text.Font; import javafx.scene.text.Text; import javafx.stage.Stage; import tray.DisclosureButton; def trayHeight = 75; def trayGradient = LinearGradient { endX: 0.0 endY: 1.0 stops: [ Stop { offset: 0.0 color: Color.web("#2b2b2b") }, Stop { offset: 1.0 color: Color.web("#1b1b1b") } ] } def contentGradient = LinearGradient { endX: 0.0 endY: 1.0 stops: [ Stop { offset: 0.0 color: Color.web("#7c8697") }, Stop { offset: 1.0 color: Color.web("#596171") } ] } Stage { var scene: Scene var rectangle: Rectangle var button: DisclosureButton var mainContentText: Text title: "Tray" width: 400 height: 300 scene: scene = Scene { fill: Color.BLACK content: [ Rectangle { y: bind scene.height - trayHeight width: bind scene.width height: trayHeight fill: trayGradient } Group { translateY: bind button.buttonPercentRotated * - trayHeight content: [ rectangle = Rectangle { width: bind scene.width height: bind scene.height fill: contentGradient effect: DropShadow { color: Color.color(0, 0, 0, 0.35) radius: 10 offsetY: 10 } }, button = DisclosureButton { translateX: 30; translateY: bind scene.height - 30 }, mainContentText = Text { fill: Color.WHITESMOKE translateX: bind scene.width / 2 - mainContentText.boundsInLocal.width / 2 translateY: bind scene.height / 2 - mainContentText.boundsInLocal.height / 2 font: Font { size: 20 } content: "Your main content here." } ] } ] } }
DisclosureButton.fx
import javafx.animation.KeyFrame; import javafx.animation.Timeline; import javafx.scene.CustomNode; import javafx.scene.Group; import javafx.scene.Node; import javafx.scene.paint.Color; import javafx.scene.shape.Circle; import javafx.scene.shape.Line; public class DisclosureButton extends CustomNode { def startButtonAngle = 0.0; def endButtonAngle = -135.0; def strokeWidth = 2; def buttonRadius = 18; def lineRadius = 12; public var buttonPercentRotated: Double = 0; var buttonRotation: Double = 0 on replace oldValue { buttonPercentRotated = buttonRotation / endButtonAngle; }; var buttonPressed = function(Void): Void { rotateTimeline.rate = rotateTimeline.rate * - 1; rotateTimeline.play(); } def firstKeyFrame = KeyFrame { time: 0ms values: buttonRotation => startButtonAngle } def lastKeyFrame = KeyFrame { time: 650ms values: buttonRotation => endButtonAngle } def rotateTimeline = Timeline { rate: -1; keyFrames: [ firstKeyFrame, lastKeyFrame ] } def buttonBackground = Circle { radius: buttonRadius; fill: Color.BLACK; stroke: Color.WHITE; strokeWidth: strokeWidth onMouseClicked: buttonPressed } def lineOne = Line { startX: -lineRadius endX: lineRadius stroke: Color.WHITE strokeWidth: strokeWidth; } def lineTwo = Line { startX: -lineRadius endX: lineRadius rotate: 90 stroke: Color.WHITE strokeWidth: strokeWidth; } def cross = Group { content: [lineOne, lineTwo] } def button = Group { content: [ buttonBackground, cross]; rotate: bind buttonRotation; } override public function create(): Node { return button; } }
March 17, 2009 at 2:32 am
Is this supposed to work on Leopard? I get a Java Web Start exception and it won’t run.
March 17, 2009 at 7:01 am
when the window is maximized it’s getting slow
March 17, 2009 at 7:50 am
Animation in JavaFX (1.1) is not on Flash’s level yet. I wrote a copy of a screen I did in flash before, and JFX definitely is not as smooth. Apparently they’re working on that.
But I’m pleased to see people start building JFX components. And aside from all the Swing-vs-JavaFX discussion; that is not much code for such a nice thingamading.
March 17, 2009 at 12:36 pm
Hi Eric,
It should work on Leopard. I’ve run it on both my home and work Leopard machine without issue.
-Ken
March 17, 2009 at 12:41 pm
Hi Tbee,
I too am assuming that Sun is working on performance issues – JavaFX is still a fresh and immature product.
In general I’ve found that writing graphics code in JavaFX is very straight forward and certainly easier and more natural than using Java 2D. The one area where there is still some pain, though, is in laying things out. That is, getting components to fill their parent and centering them, etc. is harder than it should be – but I hear they’re working on this.
-Ken
March 17, 2009 at 1:15 pm
OK, works today here at work on Vista. Nice effect! The Snap Shots popups on this blog are extremely irritating, though… :-(
March 17, 2009 at 1:46 pm
Hi Eric,
Yeah, that popup thing is annoying (it’s a WordPress “feature”). You can disable it, though.
-Ken
March 17, 2009 at 5:18 pm
Hi,
I’m very pleased to know that you are learning javaFX since your a great swing developer and I’m also learning and working on JavaFX.
The animation is also choppy on my windows vista. I don’t know if this is the same case but also most of the demos on javafx site that use interpolation get choppy animation. This also happened when I worked with java3D (also has interpolation) but I don’t know what the problem is.
Pedro
March 22, 2009 at 11:38 pm
[…] Orr , never failing to impress, has created a simple JavaFX tray component , which oozes […]