Meeting 8

Aus Java Student User Group Austria - Java + JVM in Wien Österreich / Vienna Austria
Wechseln zu: Navigation, Suche

When: Monday, December 15th, 2008 - 19:00

Where: Freihaus HS4

JavaFX - Sun Microsystem's response to Flex and Silverlight

Fehler beim Erstellen des Vorschaubildes: Die Miniaturansicht konnte nicht am vorgesehenen Ort gespeichert werden

Download the slides (pdf, 2.7MB).
You can also view the slides online.
Download the sourcecode (zip, 6.0KB).

Agenda

  • Introduction
    • History
    • Background
    • Troubles
  • Theory
    • Syntax (compared to Java)
    • Language Features
  • Showcase
  • Summary, Links

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: <source lang="javascript"> javafx.stage.Stage { title: "Hello JSUG" width: 200 height: 150 visible: true } </source>

Demos

Core-Demos

A_sequences.fx: <source lang="javascript"> 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] </source>


B_expressions.fx: <source lang="javascript"> 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 </source>


C_binding.fx: <source lang="javascript"> 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] </source>


D_classes.fx: <source lang="javascript"> 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.

}

  • /

</source>


E_exceptions.fx: <source lang="javascript"> 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)
  • /

</source>


F_fun_ction.fx: <source lang="javascript"> 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 </source>

UI-Demos

A_hello.fx: <source lang="javascript"> 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
           }
       }
   }

} </source>


B_button.fx: <source lang="javascript"> 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;
               }
           ]
       }
   }

} </source>


C_effects.fx: <source lang="javascript"> 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 }

}

</source>


D_animation.fx: <source lang="javascript"> 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);
   }

} </source>

Links

Opinions

Other JavaFX Presentations

QtJambi - Java bindings for the mighty QT-framework

Fehler beim Erstellen des Vorschaubildes: Die Miniaturansicht konnte nicht am vorgesehenen Ort gespeichert werden

Download the slides (pdf, 96KB).
You can also view the slides online.

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

Signals and slots

<source lang="java"> 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");
   }

} </source>

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).

"On a side note... Qt has been using strings for signal / slot connections for over a decade and in practice this has proven to not be a major hurdle. The reason for this is that connections are typically made during the init phase of objects and therefore always checked and fixed at an early stage." -- QtJambi FAQ

Links