Making JavaFX groovier!

Tutorial – GroovyFX

DeanIverson
GroovyFX

Dean Iverson introduces us to the Groovy flavour of JavaFX and demonstrates how it can be used to write simple JavaFX 2.0 applications

GroovyFX is a library that makes
writing JavaFX 2.x applications much simpler and more natural.
GroovyFX exploits the power of Groovy’s Builder pattern to enable
the developer to write in a concise, declarative style. The ability
to construct UIs in this way makes it easier to visualize the user
interface that is being built. Code that is short and easy to
understand is also easy to maintain and extend. GroovyFX fully
supports all of the many advanced features of JavaFX such as
controls, layout, graphics, animation, sound, video, charts, and
more.

Version 0.2 of
GroovyFX is available as a snapshot from Sonatype’s Maven
repository
. Its group ID is

org.codehaus.groovyfx and its artifact
ID is simply
groovyfx. In this
article I will be using the latest version:
0.2-SNAPSHOT.

Editor’s Note: At the time of this writing, the JavaFX
team at Oracle is finishing work on JavaFX 2.1. I anticipate that
this release will occur fairly soon after this article is
published, therefore all of the code samples in this article will
use GroovyFX 0.2-SNAPSHOT, which is the version of GroovyFX that is
tracking the changes in JavaFX 2.1.

I have recently

published a blog post
that details how to use GroovyFX in
Gradle projects and in Groovy scripts with the

@Grab annotation. Alternatively, you can clone the project
directly from GitHub and
build it yourself by following the instructions on the project’s
GitHub page (be sure you are working with the

develop branch).

Building a
Scene

A JavaFX user
interface is created by building a scene graph. Therefore, GroovyFX
has a SceneGraphBuilder class that wraps the JavaFX API classes and
makes them available using Groovy’s Builder syntax. However, you
will rarely instantiate and work with a

SceneGraphBuilder object directly.
Rather, you will typically use the static
start method of the
GroovyFX class, passing it a closure that executes your GroovyFX
code. This is illustrated by the example shown in Listing 1, which
shows a basic GroovyFX “Hello, World” program.

Listing 1: Hello World

 

import groovyx.javafx.GroovyFX

GroovyFX.start {
  stage(title: "GroovyFX Hello World", visible: true) {
    scene(fill: black, width: 530, height: 300) {
      hbox(padding: 80) {
        text(text: "Groovy", style: "-fx-font-size: 80pt") {
          fill linearGradient(endX: 0, stops: [palegreen, seagreen])
        }
        text(text: "FX", style: "-fx-font-size: 80pt") {
          fill linearGradient(endX: 0, stops: [cyan, dodgerblue])
          effect dropShadow(color: dodgerblue, radius: 25, spread: 0.25)
        }
      }
    }
  }
}

 

The primary
application container in JavaFX is the
stage. This corresponds to
the application’s window when run as a desktop application, or its
content area when run as an applet in the browser. The stage
contains a scene that keeps a reference to the root node of
the
scene graph, a data
structure that defines the content shown by the application. So
every JavaFX application is made up of a Stage, a Scene, and one or
more scene graph node classes. This structure is easy to pick out
in the GroovyFX code shown in Listing 1, where the root of the
scene graph is an instance of the JavaFX Hbox class. HBox is a
layout node that arranges its child nodes in a horizontal line.
Listing 1 shows that the root HBox node has two child Text nodes
that display the strings “Groovy” and “FX” side by side as shown
in
Figure 1.

 

Figure
1: The GroovyFX “Hello, World” program

 

The attentive
reader will no doubt have deduced the naming pattern used in
GroovyFX. Nodes in the GroovyFX builder syntax have the same names
as their corresponding JavaFX class with the first capital
letter(s) of the class name converted to lower case. Therefore the
JavaFX
Stage class is referred to as stage in GroovyFX.
Similarly
LinearGradient
becomes linearGradient,
HBox becomes hbox, Button becomes
button, and
so on.

The properties of these classes can
be set by using the property names as keys in a map that is passed
as a parameter to the builder node. The following code creates a
stage whose title property is set to “GroovyFX Hello World”.

stage(title:
“GroovyFX Hello World”)

GroovyFX also
provides some handy shortcuts for declaring your scene graph nodes
and properties. Note the linearGradient declarations in Listing 1.
GroovyFX allows you to use shortcuts such as referring to colours
by name instead of using their static values such as “Color.CYAN”.
Further, you can simply pass an array of colours as a
linearGradient’s
stops
property and GroovyFX will automatically create
evenly spaced gradient stops using those colours. These handy
shortcuts make it clean and quick to write GroovyFX
code.

Controls and Layout

JavaFX comes
with a complete set of modern UI controls such as a Button, Label,
TextField, and CheckBox. Also included are more complex controls
such as a ListView, TreeView, and TableView. JavaFX also includes
powerful set of layouts such as HBox, VBox, BorderPane, TilePane,
and the powerful and flexible GridPane. All of these controls and
layout panes are just nodes in the JavaFX scene graph. As you have
already seen in the Listing 1, any scene graph node that is a child
of a layout pane will automatically have its position and size set
by that layout pane (such as the two Text nodes that were children
of the Hbox). Technically, there are other factors that come into
play during layout, such as the value of a node’s

managed property and whether or not a
node is resizable, but in the vast majority of cases, you can
simply assume that a layout pane will control the size and
positioning of its child nodes.

Some layout panes require
additional information in order to lay out their child nodes. The
BorderPane needs to know whether a node should be placed in the
north, south, east, west, or center of its layout space. A GridPane
needs to know in which row and column to place a node. If you are
working in Java, these constraints need to be set by additional
method calls. In GroovyFX, these constraints can be passed directly
to the node’s property map and GroovyFX will take care of calling
the constraint methods for you. This allows you to keep your layout
constraints in the same place at which you declare the node and
makes the layout easier to understand. You can see an example of
this in Listing 2, where GroovyFX uses a variety of controls placed
into a GridPane in order to build a simple form.

Listing 2

 

import static groovyx.javafx.GroovyFX.start

start {
  stage(title: "GridPane Demo", width: 400, height: 500, visible: true) {
    scene(fill: groovyblue) {
      gridPane(hgap: 5, vgap: 10, padding: 25, alignment: "top_center") {
        columnConstraints(minWidth: 50, halignment: "right")
        columnConstraints(prefWidth: 250, hgrow: 'always')

        label("Please Send Us Your Feedback", style: "-fx-font-size: 18px;",
              textFill: white, row: 0, columnSpan: 2, halignment: "center",
              margin: [0, 0, 10]) {
          onMouseEntered { e -> e.source.parent.gridLinesVisible = true }
          onMouseExited { e -> e.source.parent.gridLinesVisible = false }
        }

        label("Name", hgrow: "never", row: 1, column: 0, textFill: white)
        textField(promptText: "Your name", row: 1, column: 1 )

        label("Email", row: 2, column: 0, textFill: white)
        textField(promptText: "Your email address", row: 2, column: 1)

        label("Message", row: 3, column: 0, valignment: "baseline", 
              textFill: white)
        textArea(prefRowCount: 8, row: 3, column: 1, vgrow: 'always')

        button("Send Message", row: 4, column: 1, halignment: "right")
      }
    }
  }
}

 

Listing 2
demonstrates several more GroovyFX conveniences. You will note that
the
alignment of the GridPane is set to “top_center”. GroovyFX will
figure out that you are really referring to the enumerated value
“Pos.TOP_CENTER” since GridPane’s alignment property is of
type
Pos. The same is true when setting an halignment of “center”
(HPos.CENTER).

Another
time-saving convenience allowed by GroovyFX is to define event
handlers as Groovy closures. This can be seen in the

onMouseEntered and
onMouseExited event handlers declared
on the first label node in Listing 2. These two event handlers make
the outlines of the GridPane’s cells visible when the mouse is over
the label. This can be a very handy trick when debugging layout
issues in a GridPane. The form that is created by the code in
Listing 2 is shown in
Figure
2
.

Figure
2: Creating a form layout in GroovyFX

Graphics and Animation

JavaFX is also known for its strong
graphics and animation capabilities. GroovyFX naturally has full
support in these areas as well. Take the code shown in Listing 3,
which is a GroovyFX translation of the Java-based Colorful Circles
example app written by the JavaFX team at Oracle.

Listing 3:

 

import static groovyx.javafx.GroovyFX.start

start {
  def circles
  stage(title: 'GroovyFX ColorfulCircles', resizable: false, show: true) {
    scene(width: 800, height: 600, fill: 'black') {
      group {
        circles = group {
          30.times {
            circle(radius: 200, fill: rgb(255, 255, 255, 0.05), 
                   stroke: rgb(255, 255, 255, 0.16),
                   strokeWidth: 4, strokeType: 'outside')
          }
          effect boxBlur(width: 10, height: 10, iterations: 3)
        }
        rectangle(width: 800, height: 600, blendMode: 'overlay') {
        def stops = ['#f8bd55', '#c0fe56', '#5dfbc1', '#64c2f8', 
                     '#be4af7', '#ed5fc2', '#ef504c', '#f2660f']
        fill linearGradient(start: [0f, 1f], end: [1f, 0f], stops: stops)
      }
    }
  }

  parallelTransition(cycleCount: 'indefinite', autoReverse: true) {
    def random = new Random()
    circles.children.each { circle ->
      translateTransition(40.s, node: circle, fromX: random.nextInt(800), 
                          fromY: random.nextInt(600),
                          toX: random.nextInt(800), 
                          toY: random.nextInt(600))
    }
  }.play()
}
  }
}

 

Listing 3 demonstrates
GroovyFX’s support for the JavaFX shape classes such as Circle and
Rectangle, as well as the support of all the effects such as
BoxBlur. The use of Groovy looping constructs such as

30.times to
create the circles shows one of the nice advantages of using
Groovy’s builder syntax: any Groovy expression or statement is
valid within the builder. This even includes such constructs as
if-statements that can allow you to conditionally build
nodes.

Animation is definitely one of
the strong points of JavaFX. You can define your own timelines or
use one of the many pre-packaged transition classes that are
available. Listing 3 demonstrates the use of a ParallelTransition
(a transition that runs other animation in parallel) and a
TranslateTransition (a transition that changes a node’s x,y
coordinates in order to move it around). In this case, a random
translation is applied to each of the circles so that they slowly
wander around the screen. The output of this application is shown
in
Figure 3.

Figure 3: Graphics and animation in
GroovyFX

FXBindable
Annotation

One of the very powerful capabilities of JavaFX Script (the
language used to write JavaFX 1.x applications) was its built-in
support for binding. This feature lives on in the Java-based
library of JavaFX 2.x in the form of the properties and binding API
classes. This is a fluent API that allows you to use nearly all of
the power of the old JavaFX Script binding functionality. One of
the drawbacks of the Java-based API is the amount of boilerplate
code required to define properties that are capable of being used
by the binding classes. Take, for example, the Person class shown
in Listing 4.

Listing 4 – Person Class

public class Person {
  private StringProperty firstName;
  public void setFirstName(String val) { firstNameProperty().set(val); }
  public String getFirstName() { return firstNameProperty().get(); }
  public StringProperty firstNameProperty() { 
    if (firstName == null) 
      firstName = new SimpleStringProperty(this, "firstName");
    return firstName; 
  }
 
  private StringProperty lastName;
  public void setLastName(String value) { lastNameProperty().set(value); }
  public String getLastName() { return lastNameProperty().get(); }
  public StringProperty lastNameProperty() { 
    if (lastName == null) // etc.
  } 
}

This listing shows the typical
boilerplate required to define a class with two properties in
JavaFX. These properties, since they are backed by the

StringProperty class, are able to fully participate in all of the JavaFX
binding functionality. But if you take that boilerplate and
multiply it by every property of every class in your application,
you may quickly become distressed by the amount of additional code
it introduces – code that has to be debugged and maintained by you
(or some other unfortunate developer).

GroovyFX can solve this problem
for you by taking advantage of one of Groovy’s most powerful
features, the AST transformation. Writing a custom AST transform
allows you to modify or insert code
at compile time in order to
reduce the coding burden of developers. All you have to do is
annotate a standard Groovy property with the
@FXBindable annotation and
GroovyFX will write all of that boilerplate code for you! Listing 5
shows how the
Person
class looks when using the
@FXBindable annotation. Believe it or not, this code is equivalent to
that shown in Listing 4.

Listing 5

public class Person {
  @FXBindable String firstName; 
  @FXBindable String lastName;
}

If all of the properties in your POGO should be bindable, you
can annotate the class instead of each individual property, as
shown in Listing 6.

Listing 6

@FXBindable 
public class Person {
  String firstName; 
  String lastName;
}

To learn more about
@FXBindable and GroovyFX’s binding
capabilities, I urge you to look at the
AnalogClockDemo
, which is included in the GroovyFX project on
GitHub.

Time to write a full
application?

When writing a complex application, you will typically want
support for things like using MVC design patterns, easy threading,
and perhaps an event bus. This is the domain of Griffon, an
easy-to-use Grails-like framework for writing desktop Java
applications (see
Andres Almiray’s article on Griffon)
.

You will be happy to know that moving to Griffon does not mean
that you need to give up the power of JavaFX and the convenience of
GroovyFX. A JavaFX plug-in exists for Griffon that allows you to
use GroovyFX to write the view code of your application.
Instructions for getting started are included on my GitHub page for
the Griffon JavaFX archetype. This
archetype
allows you to quickly get a new JavaFX Griffon
application up and running.

If you are interested in learning more about JavaFX and
GroovyFX, you can check out the recently published book “Pro JavaFX
2: A Definitive Guide to Rich Clients with Java Technology”, of
which I was a co-author. Oracle has also published plenty of JavaFX
documentation and examples on their JavaFX web site.

Author Bio: 

Dean Iverson has been writing software professionally
for more than 15 years. He is employed by the Virginia Tech
Transportation Institute, where he is a rich client application
developer. He has been working with JavaFX technologies from the
beginning and is a contributor to the GroovyFX and JFXtras open
source projects as well as being a co-author of the Pro JavaFX
books published by Apress.

This article originally appeared in Java Tech Journal – The
Groovy Universe. Check out more
Groovy projects there

 

Author
DeanIverson
Dean Iverson has been writing software professionally for more than 15 years. He is employed by the Virginia Tech Transportation Institute, where he is a rich client application developer. He has been working with JavaFX technologies from the beginning and is a contributor to the GroovyFX and JFXtras open source projects as well as being a co-author of the Pro JavaFX books published by Apress.
Comments
comments powered by Disqus