Meeting 8

Aus JSUG

Wechseln zu: Navigation, Suche

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

Where: Freihaus HS4

Inhaltsverzeichnis

[Bearbeiten] JavaFX - Sun Microsystem's response to Flex and Silverlight

Logo

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

[Bearbeiten] Opinions

[Bearbeiten] Other JavaFX Presentations

[Bearbeiten] QtJambi - Java bindings for the mighty QT-framework

Logo

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

"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

[Bearbeiten] Links

Persönliche Werkzeuge