Meeting 8
Aus JSUG
When: Monday, December 15th, 2008 - 19:00
Where: Freihaus HS4
Inhaltsverzeichnis |
[Bearbeiten] JavaFX - Sun Microsystem's response to Flex and Silverlight
Download the slides (pdf, 2.7MB).
You can also view the slides online.
Download the sourcecode (zip, 6.0KB).
[Bearbeiten] Agenda
- Introduction
- History
- Background
- Troubles
- Theory
- Syntax (compared to Java)
- Language Features
- Showcase
- Summary, Links
[Bearbeiten] Content
Christoph Pickl presented JavaFX (earlier known as F3: Form Follows Function) which was just recently released as a stable version by Sun Microsystems. It is open source and consists of the core language "JavaFX Script" and JavaFX Mobile which is a Java operating system for mobile devices sitting on top of a Linux kernel. JavaFX targets the Rich Internet Application (RIA) domain competing with Adobe Flex and Microsoft's Silverlight, and is primarly intended for Swing- and Webdevelopers.
Instead of being yet another declarative XML language, JavaFX Script looks like JSON and can be either declarative or procedural (object-oriented). It is static typed with type inference, so declaration is not mandatory. Many things will be familiar to you but also new features were introduced (e.g.: binding, sequences and object literal notation).
The smallest possible JavaFX application:
javafx.stage.Stage { title: "Hello JSUG" width: 200 height: 150 visible: true }
[Bearbeiten] Demos
[Bearbeiten] Core-Demos
A_sequences.fx:
package jsug.javafx; // simple sequence creation println(["foo", "bar"]); // => [foo, bar] //var x: String[] = ["foo", "bar"]; // same with nested sequences (will be flattened) println(["foo", ["bar"]]); // => [foo, bar] // access via index println(["first", "second", "third"][1]); // => "second" // special sizeof operator println(sizeof [0, 1, 2]); // => 3 // create range println([0..3]); // => [0, 1, 2, 3] // manually set step size println([0..6 step 2]); // => [ 0, 2, 4, 6 ] println([6..0 step -2]); // => [ 6, 4, 2, 0 ] // use list comprehension functionality println([0..5][x | x mod 2 == 0]); // => [0, 2, 4] // manipulate sequences with insert/delete var x = [0, 2]; insert 3 into x; println(x); // => [0, 2, 3] insert 1 before x[1]; // after x[0] println(x); // => [0, 1, 2, 3] delete 2 from x; println(x); // => [0, 1, 3] delete x[2]; println(x); // => [0, 1] x = reverse x; println(x); // => [1, 0] delete x; println(x); // => [] // sequence slices var y = [0..4]; println(y[0..2]); // => [0, 1, 2] println(y[0..<2]); // => [0, 1] println(y[2..]); // => [2, 3, 4] println(y[2..<]); // => [2, 3]
B_expressions.fx:
package jsug.javafx; // expression ("anonymous function") returns last result var x = { var s: String = "Jsug"; "Hello {s}!" }; println(x); // => "Hello Jsug!" // if can be shortened, because its an expression too var i = 20; var y = if(i < 10) 1 else if(i < 100) 2 else 3; println(y); // => 2 // for loop also returns a value var z = for(j in [0..5]) j * 2; println(z); // => [ 0, 2, 4, 6, 8, 10 ] // while, break, continue ... returning Void
C_binding.fx:
package jsug.javafx.core; // ------------------------------------------------ // bind value to string expression var greeting = "Mr"; var name = "Johnson"; def x = bind "Hello {greeting} {name}!"; println(x); // => "Hello Mr Johnson!" greeting = "Ms"; name = "Tailor"; println(x); // => "Hello Ms Tailor!" // ------------------------------------------------ // bind value to an expression var n = 8; def y = bind { n / 2; }; println(y); // => 4 n = 84; println(y); // => 42 // ------------------------------------------------ // bind value to function function f(x: Integer, y: Integer): Integer { x + y; } var v: Integer = 6; var w: Integer = 4; def z = bind f(v, w); println(z); // => 10 v = 38; println(z); // => 42 // ------------------------------------------------ // use triggers var triggered: String = "N/A" on replace oldTriggered { println("triggered from [{oldTriggered}] to [{triggered}]"); } // => "triggered from [] to [N/A]" triggered = "new value"; // => "triggered from [N/A] to [new value]" // ------------------------------------------------ // bind on objects ... // ... see classes.fx // ------------------------------------------------ // bound functions var suffix: String = "MB"; bound function f2(x: Integer): String { "({x} {suffix})" } def suffixedNumber = bind f2(12); println(suffixedNumber); // => "(12 MB)" suffix = "GB"; println(suffixedNumber); // => "(12 GB)" // ------------------------------------------------ // bind on sequences var singled = [0..3]; var doubled = bind for(i in singled) i * 2; println(doubled); // => [0, 2, 4, 6] singled[3] = 21; println(doubled); // => [0, 2, 4, 42]
D_classes.fx:
package jsug.javafx.core; import java.awt.event.ActionListener; import java.awt.event.ActionEvent; import javax.swing.JButton; // ------------------------------------------------ // define class with attributes and operations class Person { public var name: String; public var age: Integer; public function printAttributes() { println("{this.name} is {this.age} years old."); } } def otto = Person { name: "Otto" age: 18 } otto.printAttributes(); // => "Otto is 18 years old." // ------------------------------------------------ // multiple inheritance class X { var foo: String; } class Y { var bar: String; // foo: Integer ?! => compile error } class Z extends X, Y { } def z = Z { foo: "bar" bar: "foo" } // ------------------------------------------------ // abstract classes/methods abstract class AbstractService { abstract function doSomething(): Void; } class ConcreteService extends AbstractService { override function doSomething(): Void { // doing stuff here } } def service: AbstractService = ConcreteService {}; service.doSomething(); // ------------------------------------------------ // visibility class Visibilities { var onlyWithinFxFile; // script-private by default package var onlyWithinSamePackage; protected var onlyPackageAndInherited; public-read var readEverywhereWriteScriptPrivate; public-read package var readEverywhereWriteWithinPackage; public-init var readEverywhereWriteOnlyDuringCreation; // public-init var package ... public var everyoneEverything; } // ------------------------------------------------ // javafx and java class MyListener extends ActionListener { override public function actionPerformed(evt: ActionEvent) { println("Action Performed"); } } def btn: JButton = new JButton(); btn.addActionListener(MyListener {}); btn.doClick(); // => ""Action Performed"" // ------------------------------------------------ // binding on objects (or attributes only) var myName = "Foo"; def p = bind Person { name: myName; } println(p.name); // => "Foo" myName = "Bar"; // ... komplett neues objekt wird erzeugt!!! println(p.name); // => "Bar" /* def p2 = Person { name: bind myName; ... person objekt bleibt gleich. } */
E_exceptions.fx:
package jsug.javafx; import java.lang.IllegalArgumentException; import java.lang.Exception; function foo(i) { if(i < 0) { throw new IllegalArgumentException("i < 0"); } // ... some more code ... } try { foo(-1) } catch(e: Exception) { e.printStackTrace(); } /* java.lang.IllegalArgumentException: i < 0 at jsug.javafx.exceptions.foo(exceptions.fx:9) at jsug.javafx.exceptions.foo(exceptions.fx:9) at jsug.javafx.exceptions.javafx$run$(exceptions.fx:16) at jsug.javafx.exceptions.javafx$run$(exceptions.fx:16) at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method) at sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:39) at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:25) at java.lang.reflect.Method.invoke(Method.java:585) at com.sun.javafx.runtime.provider.AWT_EDT_RuntimeProvider$1.run(AWT_EDT_RuntimeProvider.java:104) at java.awt.event.InvocationEvent.dispatch(InvocationEvent.java:209) at java.awt.EventQueue.dispatchEvent(EventQueue.java:461) at java.awt.EventDispatchThread.pumpOneEventForHierarchy(EventDispatchThread.java:269) at java.awt.EventDispatchThread.pumpEventsForHierarchy(EventDispatchThread.java:190) at java.awt.EventDispatchThread.pumpEvents(EventDispatchThread.java:184) at java.awt.EventDispatchThread.pumpEvents(EventDispatchThread.java:176) at java.awt.EventDispatchThread.run(EventDispatchThread.java:110) */
F_fun_ction.fx:
package jsug.javafx.core; // do fun with functions as first class citizens function addN(n: Number): function(:Number): Number { function(x: Number): Number { x + n } } println(addN(10)(3)); // => 13.0
[Bearbeiten] UI-Demos
A_hello.fx:
package jsug.javafx.ui; import javafx.stage.Stage; import javafx.scene.Scene; import javafx.scene.text.Text; import javafx.scene.text.Font; Stage { title: "Hello" width: 400 height: 200 scene: Scene { content: Text { x: 80 y: 100 content: "Hello JSUG" font: Font { size: 48 // in pt } } } }
B_button.fx:
package jsug.javafx.ui; import javafx.stage.Stage; import javafx.ext.swing.SwingButton; import javafx.scene.Scene; import javafx.scene.text.Text; import javafx.scene.layout.VBox; import java.util.Date; var labelText = "no text yet"; Stage { title: "Button Demo" width: 220 height: 80 scene: Scene { content: VBox { content: [ SwingButton { text: "Click Me" action: function() { println("Button pressed"); this.labelText = new Date().toString(); } }, Text { content: bind this.labelText; translateY: 12; } ] } } }
C_effects.fx:
package jsug.javafx.ui; import javafx.scene.Group; import javafx.scene.effect.DropShadow; import javafx.scene.paint.Color; import javafx.scene.shape.Rectangle; import javafx.scene.shape.Circle; import javafx.stage.Stage; import javafx.scene.Scene; def group = Group { effect: DropShadow { offsetX: 5 offsetY: 5 color: Color.BLACK; } content: [ Rectangle { x: 10, y: 20 width: 40 height: 40 fill: Color.GREEN }, Circle { radius: 20 centerX: 90 centerY: 40 fill: Color.BLUE; } ] } Stage { title: "Effects" width: 110 height: 110 visible: true scene: Scene { content: group } }
D_animation.fx:
package jsug.javafx.ui; import javafx.animation.Interpolator; import javafx.animation.Timeline; import javafx.stage.Stage; import javafx.scene.Scene; import javafx.scene.paint.Color; import javafx.scene.image.Image; import javafx.scene.image.ImageView; import javafx.scene.shape.ArcTo; import javafx.scene.shape.MoveTo; import javafx.scene.shape.Path; import javafx.scene.effect.Lighting; import javafx.scene.effect.light.DistantLight; var x: Number; var y: Number; // setup x-movement Timeline { repeatCount: Timeline.INDEFINITE autoReverse: true keyFrames: [ at (0s) {x => 0.0}, at (7s) {x => 387.0 tween Interpolator.LINEAR}, ] }.play(); // setup y-movement Timeline { repeatCount: Timeline.INDEFINITE autoReverse: true keyFrames: [ at (0s) {y => 0.0}, at (4s) {y => 55.0 tween Interpolator.LINEAR}, ] }.play(); def bgImage = ImageView{ image: Image{ url: "http://java.sun.com/docs/books/tutorial/2d/basic2d/examples/images/weather-sun.png" } } def cloud = Path { translateX: bind x translateY: bind y fill: Color.WHITE stroke: Color.LIGHTBLUE strokeWidth: 2 effect: Lighting{light: DistantLight{azimuth: 90}} elements: [ MoveTo { x: 15 y: 15 }, ArcTo { x: 50 y: 10 radiusX: 20 radiusY: 20 sweepFlag: true}, ArcTo { x: 70 y: 20 radiusX: 20 radiusY: 20 sweepFlag: true}, ArcTo { x: 50 y: 60 radiusX: 20 radiusY: 20 sweepFlag: true}, ArcTo { x: 20 y: 50 radiusX: 10 radiusY: 5 sweepFlag: true}, ArcTo { x: 15 y: 15 radiusX: 10 radiusY: 10 sweepFlag: true}, ] } Stage{ title: "Cloud" visible: true scene: Scene { fill: Color.WHITE content: [ bgImage, cloud ] } onClose: function() { java.lang.System.exit(0); } }
[Bearbeiten] Links
- javafx.com - Official Website
- Tutorial Core - Learning the JavaFX Script Programming Language
- Tutorial UI - Building GUI Applications with JavaFX
- API Doc - Oficial JavaFX 1.0 API Documentation
- Java News Brief Dec 08 - Very good JavaFX Tutorial
[Bearbeiten] Opinions
- JavaFX Launch and Interview - Java Posse Audiocast
- JavaFX Technology Preview - Article by Charles Humble
- Learn JavaFX - Blog from James Weaver
- Too little, too late, too bad - Blog by Alex Blewitt
- Watching Java presentations with AJAX, Flex, AIR and JavaFX - Interview with Stephan Janssen (parleys.com)
[Bearbeiten] Other JavaFX Presentations
- Introduction to JavaFX - Outdated Presentation from James Weaver
- A tour of the landscape - James Gosling talking about Java(FX)
[Bearbeiten] QtJambi - Java bindings for the mighty QT-framework
Download the slides (pdf, 96KB).
You can also view the slides online.
[Bearbeiten] Content
Jan Zarnikov introduced a possibility to write Java applications (could have also be Python, Perl, PHP, ...) using the well-known QT-framework. QT is a powerful C++ framework, providing features for GUI, XML, database, IO, network, threads and so on and runs on many platforms (including embedded systems) with its native look&feel.
Other cool features would be:
- Easy WebKit integration (HTML rendering)
- Native libraries; simple deployment as JARs or WebStart
- Phonon integration; crossplatform multimedia playback (not that easy in Java)
- Styling with CSS
Some downsides:
- Sometimes not "the Java way"
- Overlapping functionality with standard Java classes
- Debugging of native libraries difficult
- Availability of document and tutorials
[Bearbeiten] Signals and slots
public class MyGUI { private QPushButton button; public MyGUI() { button = new QPushButton("Go!"); button.clicked.connect(this,"doSomething()"); } public void doSomething() { System.out.println("hello world"); } }
Although this concept is not really the Java way of doing things, it results in fewer LoC and the passing of arguments is made possible. The concept relies on public fields and slots which references method names as Strings (no compile time checking; refactoring).
[Bearbeiten] Links
- http://trolltech.com/ ... Official Website
- http://www.qtforum.de/forum/ ... German Forum


