JavaFX 2 and Scala - Like Milk and Cookies (33rd Degrees)
-
Upload
stephen-chin -
Category
Technology
-
view
52.085 -
download
0
description
Transcript of JavaFX 2 and Scala - Like Milk and Cookies (33rd Degrees)
JavaFX 2.0 and Scala, Like Milk and Cookies Stephen Chin
Chief Agile Methodologist, [email protected]: @steveonjava
Meet the Presenter
> Chief Agile Methodologist, GXS
> Java Champion> Open Source Hacker
JFXtras ScalaFX Visage
> User Group Leader Silicon Valley JavaFX User
Group Streamed Live!
Stephen Chin
Motorcyclist
Family Man
JavaFX 2.0 Platform
Immersive Application Experience
> Cross-platform Animation, Video, Charting
> Integrate Java, JavaScript, and HTML5 in the same application
> New graphics stack takes advantage of hardware acceleration for 2D and 3D applications
> Use your favorite IDE: NetBeans, Eclipse, IntelliJ, etc.
Programming Languages
> JavaFX Script is no longer supported by Oracle Existing JavaFX Script based applications will
continue to run Visage is the open-source successor to the JavaFX
Script language> JavaFX 2.0 APIs are now in Java
Pure Java APIs for all of JavaFX Binding and Sequences exposed as Java APIs FXML Markup for tooling
5
JavaFX
Scala
JavaFX With Java
JavaFX in Java
> JavaFX API uses an enhanced JavaBeans pattern
> Similar in feel to other UI toolkits (Swing, Pivot, etc.)
> Uses builder pattern to minimize boilerplate
Example Application
public class HelloStage extends Application {
@Override public void start(Stage stage) { stage.setTitle("Hello Stage"); stage.setWidth(600); stage.setHeight(450);
Group root = new Group(); Scene scene = new Scene(root); scene.setFill(Color.LIGHTGREEN);
stage.setScene(scene); stage.show(); }
public static void main(String[] args) { Application.launch(args); }}
Example Application Using Builders
public class HelloStage extends Application {
@Override public void start(Stage stage) { stage.setTitle("Hello Stage"); stage.setScene(SceneBuilder.create() .fill(Color.LIGHTGREEN) .width(600) .height(450) .build()); stage.show(); }
public static void main(String[] args) { Application.launch(args); }}
Observable Properties
> Supports watching for changes to properties
> Implemented via anonymous inner classes
> Will take advantage of closures in the future
Observable Pseudo-Properties
final Rectangle rect = new Rectangle();rect.setX(40);rect.setY(40);rect.setWidth(100);rect.setHeight(200);
rect.hoverProperty().addListener(new ChangeListener<Boolean>() {
});
Observable Pseudo-Properties
final Rectangle rect = new Rectangle();rect.setX(40);rect.setY(40);rect.setWidth(100);rect.setHeight(200);
rect.hoverProperty().addListener(new ChangeListener<Boolean>() {
});
The property we want to watch
Observable Pseudo-Properties
final Rectangle rect = new Rectangle();rect.setX(40);rect.setY(40);rect.setWidth(100);rect.setHeight(200);
rect.hoverProperty().addListener(new ChangeListener<Boolean>() {
});
Only one listener used with generics to specify the data type
Observable Pseudo-Properties
final Rectangle rect = new Rectangle();rect.setX(40);rect.setY(40);rect.setWidth(100);rect.setHeight(200);
rect.hoverProperty().addListener(new ChangeListener<Boolean>() { public void changed(ObservableValue<? extends Boolean> property, Boolean oldValue, Boolean value) {
}});
Refers to the Rectangle.hoverProperty()
Observable Pseudo-Properties
final Rectangle rect = new Rectangle();rect.setX(40);rect.setY(40);rect.setWidth(100);rect.setHeight(200);
rect.hoverProperty().addListener(new ChangeListener<Boolean>() { public void changed(ObservableValue<? extends Boolean> property, Boolean oldValue, Boolean value) { rect.setFill(rect.isHover() ? Color.GREEN : Color.RED); }});
Binding
> Unquestionably the biggest JavaFX Script innovation
> Supported via a PropertyBinding class> Lazy invocation for high performance> Static construction syntax for simple
cases e.g.: bind(<property>),
bindBiDirectional(<property>)
Sequences in Java
> Replaced with an Observable List
> Public API is based on JavaFX sequences
> Internal code can use lighter collections API
> JavaFX 2.0 also has an Observable Map
18
Vanishing Circles
Application Skeleton
public class VanishingCircles extends Application { public static void main(String[] args) { Application.launch(args); } @Override public void start(Stage primaryStage) { primaryStage.setTitle("Vanishing Circles"); Group root = new Group(); Scene scene = new Scene(root, 800, 600, Color.BLACK); [create the circles…] root.getChildren().addAll(circles); primaryStage.setScene(scene); primaryStage.show(); [begin the animation…] }}
Create the Circles
List<Circle> circles = new ArrayList<Circle>();for (int i = 0; i < 50; i++) { final Circle circle = new Circle(150); circle.setCenterX(Math.random() * 800); circle.setCenterY(Math.random() * 600); circle.setFill(new Color(Math.random(), Math.random(), Math.random(), .2)); circle.setEffect(new BoxBlur(10, 10, 3)); circle.setStroke(Color.WHITE); [setup binding…] [setup event listeners…] circles.add(circle);}
20
Setup Binding
circle.strokeWidthProperty().bind(Bindings .when(circle.hoverProperty()) .then(4) .otherwise(0));
21
Setup Event Listeners
circle.addEventHandler(MouseEvent.MOUSE_CLICKED, new EventHandler<MouseEvent>() { public void handle(MouseEvent t) { KeyValue collapse = new KeyValue(circle.radiusProperty(), 0); new Timeline(new KeyFrame(Duration.seconds(3), collapse)).play(); }});
22
Begin the Animation
Timeline moveCircles = new Timeline();for (Circle circle : circles) { KeyValue moveX = new KeyValue(circle.centerXProperty(), Math.random() * 800); KeyValue moveY = new KeyValue(circle.centerYProperty(), Math.random() * 600); moveCircles.getKeyFrames().add(new KeyFrame(Duration.seconds(40), moveX, moveY));}moveCircles.play();
23
24
JavaFX With Scala
25
What is Scala
> Started in 2001 by Martin Odersky> Compiles to Java bytecodes> Pure object-oriented language> Also a functional programming language
2001• Scala Started
2003/2004• Scala v1.0
2006• Scala v2.0
2011• Scala 2.9.1
2012• Scala 2.9.1-1
(latest)
26
Why Scala?
> Shares many language features with JavaFX Script that make GUI programming easier: Static Type Checking – Catch your errors at compile
time Closures – Wrap behavior and pass it by reference Declarative – Express the UI by describing what it
should look like
> Scala also supports Type Safe DSLs! Implicit Conversions – type safe class extension Operator Overloading – with standard precedence
rules
Java vs. Scala DSLpublic class VanishingCircles extends Application {
public static void main(String[] args) { Application.launch(args); } @Override public void start(Stage primaryStage) { primaryStage.setTitle("Vanishing Circles"); Group root = new Group(); Scene scene = new Scene(root, 800, 600, Color.BLACK); List<Circle> circles = new ArrayList<Circle>(); for (int i = 0; i < 50; i++) { final Circle circle = new Circle(150); circle.setCenterX(Math.random() * 800); circle.setCenterY(Math.random() * 600); circle.setFill(new Color(Math.random(), Math.random(),
Math.random(), .2)); circle.setEffect(new BoxBlur(10, 10, 3)); circle.addEventHandler(MouseEvent.MOUSE_CLICKED, new
EventHandler<MouseEvent>() { public void handle(MouseEvent t) { KeyValue collapse = new KeyValue(circle.radiusProperty(), 0); new Timeline(new KeyFrame(Duration.seconds(3), collapse)).play(); } }); circle.setStroke(Color.WHITE);
circle.strokeWidthProperty().bind(Bindings.when(circle.hoverProperty()) .then(4) .otherwise(0)); circles.add(circle); } root.getChildren().addAll(circles); primaryStage.setScene(scene); primaryStage.show(); Timeline moveCircles = new Timeline(); for (Circle circle : circles) { KeyValue moveX = new KeyValue(circle.centerXProperty(), Math.random()
* 800); KeyValue moveY = new KeyValue(circle.centerYProperty(), Math.random()
* 600); moveCircles.getKeyFrames().add(new KeyFrame(Duration.seconds(40),
moveX, moveY)); } moveCircles.play(); }}
object VanishingCircles extends JFXApp { var circles: Seq[Circle] = null stage = new Stage { title = "Vanishing Circles" width = 800 height = 600 scene = new Scene { fill = BLACK circles = for (i <- 0 until 50) yield new Circle { centerX = random * 800 centerY = random * 600 radius = 150 fill = color(random, random, random, .2) effect = new BoxBlur(10, 10, 3) strokeWidth <== when (hover) then 4 otherwise 0 stroke = WHITE onMouseClicked = { Timeline(at (3 s) {radius -> 0}).play() } } content = circles } }
new Timeline { cycleCount = INDEFINITE autoReverse = true keyFrames = for (circle <- circles) yield at (40 s) { Set( circle.centerX -> random * stage.width, circle.centerY -> random * stage.height ) } }.play();}
27
40 Lines1299 Characters
33 Lines591 Characters
object VanishingCircles extends JFXApp { stage = new Stage { title = "Disappearing Circles" width = 800 height = 600 scene = new Scene { fill = BLACK content = for (i <- 0 until 50) yield new Circle {
centerX = random * 800 centerY = random * 600 radius = 150 fill = color(random, random, random, 0.2) effect = new BoxBlur(10, 10, 3) } } }}
28
29
object VanishingCircles extends JFXApp { stage = new Stage { title = "Disappearing Circles" width = 800 height = 600 scene = new Scene { fill = BLACK content = for (i <- 0 until 50) yield new Circle { centerX = random * 800 centerY = random * 600 radius = 150 fill = color(random, random, random, 0.2) effect = new BoxBlur(10, 10, 3) } } }}
Base class for JavaFX applications
30
object VanishingCircles extends JFXApp { stage = new Stage { title = "Disappearing Circles" width = 800 height = 600 scene = new Scene { fill = BLACK content = for (i <- 0 until 50) yield new Circle {
centerX = random * 800 centerY = random * 600 radius = 150 fill = color(random, random, random, 0.2) effect = new BoxBlur(10, 10, 3) } } }}
Declarative Stage definition
31
object VanishingCircles extends JFXApp { stage = new Stage { title = "Disappearing Circles" width = 800 height = 600 scene = new Scene { fill = BLACK content = for (i <- 0 until 50) yield new Circle {
centerX = random * 800 centerY = random * 600 radius = 150 fill = color(random, random, random, 0.2) effect = new BoxBlur(10, 10, 3) } } }}
Inline property definitions
32
object VanishingCircles extends JFXApp { stage = new Stage { title = "Disappearing Circles" width = 800 height = 600 scene = new Scene { fill = BLACK content = for (i <- 0 until 50) yield new Circle {
centerX = random * 800 centerY = random * 600 radius = 150 fill = color(random, random, random, 0.2) effect = new BoxBlur(10, 10, 3) } } }}
Sequence Creation Via Loop
Binding in Scala
Infix Addition/Subtraction/Multiplication/Division:
height <== rect1.height + rect2.height
Aggregate Operators:
width <== max(rect1.width, rect2.width, rect3.width)
Conditional Expressions:
strokeWidth <== when (hover) then 4 otherwise 0
Compound Expressions:
text <== when (rect.hover || circle.hover && !disabled) then textField.text + " is enabled" otherwise "disabled"
33
Animation in Scala
val timeline = new Timeline { cycleCount = INDEFINITE autoReverse = true keyFrames = for (circle <- circles) yield at (40 s) {
Set( circle.centerX -> random * stage.width, circle.centerY -> random * stage.height ) }}timeline.play();
34
val timeline = new Timeline { cycleCount = INDEFINITE autoReverse = true keyFrames = for (circle <- circles) yield at (40 s) {
Set( circle.centerX -> random * stage.width, circle.centerY -> random * stage.height ) }}timeline.play();
Animation in Scala
35
JavaFX Script-like animation syntax: at (duration) {keyframes}
val timeline = new Timeline { cycleCount = INDEFINITE autoReverse = true keyFrames = for (circle <- circles) yield at (40 s) {
Set( circle.centerX -> random * stage.width, circle.centerY -> random * stage.height ) }}timeline.play();
Animation in Scala
36
Operator overloading for animation syntax
val timeline = new Timeline { cycleCount = INDEFINITE autoReverse = true keyFrames = for (circle <- circles) yield at (40 s) {
Set( circle.centerX -> random * stage.width tween EASE_BOTH,
circle.centerY -> random * stage.height tween EASE_IN
) }}timeline.play();
Animation in Scala
37
Optional tween syntax
Event Listeners in Scala
38
> Supported using the built-in Closure syntax> Optional arguments for event objects> 100% type-safe
onMouseClicked = { Timeline(at(3 s){radius->0}).play()
}
Event Listeners in Scala
> Supported using the built-in Closure syntax> Optional arguments for event objects> 100% type-safe
39
Compact syntax{body}
onMouseClicked = { Timeline(at(3 s){radius->0}).play()
}
Event Listeners in Scala
> Supported using the built-in Closure syntax> Optional arguments for event objects> 100% type-safe
40
Optional event parameter
{(event) => body}
onMouseClicked = { (e: MouseEvent) =>
Timeline(at(3 s){radius->0}).play()
}
a.k.a. How to Write Your Own Scala DSL
41
ScalaFX Internals
With quotes from Stephen Colebourne (@jodastephen) to help us keep our sanity!Disclaimer: Statements taken from http://blog.joda.org and may not accurately reflect his opinion or viewpoint.
Luc Viatour / www.Lucnix.be
42
Application Initialization
> JavaFX Requires all UI code executed on the Application Thread
> But our ScalaFX Application has no start method:
object VanishingCircles extends JFXApp {
stage = new Stage { … }}
How Does This Code Work?!?
43
DelayedInit
> Introduced in Scala 2.9> How to Use It:1. Extend a special trait called DelayedInit2. Implement a method of type:
def delayedInit(x: => Unit): Unit3. Store off the init closure and call it on the
Application Thread
For me, Scala didn't throw enough away and added too much - a lethal combination.
Joda says…
Hierarchical Implicit Conversions
> ScalaFX defines a set of proxies that mirror the JavaFX hierarchy
> JavaFX classes are "implicitly" wrapped when you call a ScalaFX API
> But Scala implicit priority ignores type hierarchy!
44
JFXNode
JFXShape
JFXCircle
SFXNode
SFXShape
SFXCircle
?
!
N-Level Implicit Precedence
> Scala throws an exception if two implicits have the same precedence
> Classes that are extended have 1 lower precedence:
> You can stack extended traits n-levels deep to reduce precision by n
45
Well, it may be type safe, but its also silent and very deadly.
Joda says…
object HighPriorityIncludes extends LowerPriorityIncludes {…}trait LowerPriorityIncludes {…}
Properties
> JavaFX supports properties of type Boolean, Integer, Long, Float, Double, String, and Object
> Properties use Generics for type safety> But generics don't support primitives…
> JavaFX solves this with 20 interfaces and 44 classes for all the type/readable/writable combinations.
> Can we do better?
46
@specialized
> Special annotation that generates primitive variants of the class
> Improves performance by avoiding boxing/unboxing
> Cuts down on code duplication (ScalaFX only has 18 property/value classes total)
47
Whatever the problem, the type system is bound to be part of the solution.
Joda says…
trait ObservableValue[@specialized(Int, Long, Float, Double, Boolean) T, J]
Bindings
48
> How does Scala know what order to evaluate this in?
text <== when (rect.hover || circle.hover && !disabled) then textField.text + " is enabled" otherwise "disabled
And why the funky bind operator?!?
Operator Precedence Rules
49
> First Character Determines Precedence
Highest Precedence
10. (all letters)9. |8. ^7. &6. < >5. = !4. :3. + *2. / %1. (all other special characters)
Lowest Precedence
11. Assignment Operators end with equal> But don't start with equal> And cannot be one of:
<= >= !=
Exception Assignment Operators, which are even lower…
Operator Precedence
50
Personally, I find the goal of the open and flexible syntax (arbitrary DSLs) to be not worth the pain
Joda says…
text <== when (rect.hover || circle.hover
&& !disabled) then textField.text + " is
enabled" otherwise "disabled"
911 10
7 105 3
10
Conclusion
> You can use Scala and JavaFX together.> ScalaFX provides cleaner APIs that are tailor
designed for Scala.> Try using ScalaFX today and help contribute APIs
for our upcoming 1.0 release!
http://code.google.com/p/scalafx/