Friday Jun 19, 2009

FXD Details (1): FXZ Loading

JavaFX Content File Loading

Once you export your graphic assets from the Adobe Photoshop or Illustrator, you will need to load it to the JavaFX. Here are the various ways loading it depending on what you want to do.  

1) Synchronous Load

JavaFX content file (FXZ) or data file (FXD) can be loaded synchronously by using the following approaches. As FX event thread will be blocked until synchronous loading is completed, this approach maybe good for the local loading only.

A) FXDLoader.load():Node

The simplest form. It is useful when the content of the FXZ file is ready for use as it is, that is, no need of manipulation such as scaling, adding effects, etc from JavaFX code. The returned node is actually a Group node whose parent is null. It can be directly added to another branch of the scene. 

Example 1:
insert FXDLoader.load("{__DIR__}fxsample.fxz") into scene.content;

B) FXDLoader.loadContent():FXDContent

This approach is usually used when the content of the FXZ file needs some touch-up from JavaFX code such as scaling, adding effects, etc. Note that all other getter functions except getRoot() return a child node whose parent is already set to the top level Group in the FXZ content. Since a node with a non-null parent can not be reused in a different parent, the node should be removed from the old parent or its clone should be used. The Duplicator class comes in place for this purpose.

 Example 1:
 var fxdContent:FXDContent = FXDLoader.loadContent("{__DIR__}fxsample.fxz");
 scene.content = fxdContent.getRoot();

 Example 2:
var fxdContent:FXDContent = FXDLoader.loadContent("{__DIR__}fxsample.fxz");
 scene.content = Duplicator.duplicate(fxdContent.getNode("atom"));

2) Asynchronous Load

JavaFX content file (FXZ) or data file (FXD) can be loaded asynchronously as well by using the following approaches. This approach is recommended for the network loading.

C) FXDLoader.loadOnBackground(url, loader):FXDLoader

This is useful when you want to reuse the same loader to load more than 1 FXZ file as illustrated in the example.

Example 1:
var loader:FXDLoader = FXDLoader {
    onDone: function() {
        if(loader.succeeded) {
            scene.content = loader.content.getRoot();
        }
    }
}
FXDLoader.loadOnBackground("{__DIR__}fxsample.fxz", loader);

PauseTransition {
    duration: 3s
    action: function() {
        FXDLoader.loadOnBackground("{__DIR__}fifteen.fxz", loader);
    }
}.play();

D) FXDLoader.createLoader(url):FXDLoader

FXDLoader is extended from javafx.async.Task. If you are familiar with the FX async framework, the following approach should make you feel at home. Please see my other blog about FX Async Operation for details.

 Example 1:
var loader:FXDLoader = FXDLoader.createLoader("{__DIR__}fxsample.fxz");
loader.onDone = function() {
    if(loader.succeeded) {
        insert loader2.content.getRoot() into scene.content;
    }
}
loader.start();

2) Configurable Synchronous/Asynchronous Load

FXDNode can be configured to load synchronously or asynchronously by using the following approaches. NetBeans IDE has a special support to create a UI class that extends FXDNode for the selected FXZ file. This approach is preferred in most of usecases.

e) FXDNode {backgroundLoading: false} - Synchronous Loading

As in the next case, a loader can be attached only for listening events such as onStart, onDone, etc.

Example 1:
scene.content = FXDNode {
    url: "{__DIR__}fxsample.fxz"
    backgroundLoading: false
    placeholder: Text{ x:10 y: bind scene.height-50 content: "Loading graphics..." fill: Color.YELLOW}
}

f) FXDNode {backgroundLoading: true} - Asynchronous Loading

A loader can be attached but it is only for listening events such as onStart, onDone, etc. Note that loader.source and loader.content don't have what you might be expecting but null values.

Example 1:
var loader: FXDLoader;
scene.content = FXDNode {
    url: "{__DIR__}fxsample.fxz"
    backgroundLoading: true
    placeholder: Text{ x:10 y: bind scene.height-50 content: "Loading graphics..." fill: Color.YELLOW}
    loader: loader = FXDLoader {
        onStart: function() {
            println("onStart");
        }
        onDone: function() {
            println("onDone: {loader.source}");             // onDone: null
            println("onDone: {loader.content.getRoot()}");
  // onDone: null
        }
    }
}


g) Generating a UI Stub File for a JavaFX Graphic in the NetBeans IDE

When a JavaFX Content File (FXZ) is included in a NetBeans project, developers can generate a UI Stub file, which enables them to easily access the graphical elements from JavaFX applications. This UI Stub file defines variables for all of the graphic objects with an ID value. The UI stub file that is generated has the same name as the JavaFX graphic plus a UI suffix. For example, if the JavaFX graphic is named fxsample.fxz, the stub file is named fxsampleUI.fx. The class that is generated also has the name fxsampleUI.

  1. In the NetBeans IDE, open the project for your JavaFX application.
  2. Locate the JavaFX Content File (FXZ) in the Projects pane.
  3. Right-click (or Control-click on the Mac) the JavaFX Content File (FXZ) and select Generate UI stub.

Note that the generated UI class extends FXDNode so its usage is same as above. It's convenient to access the enclosed graphic assests.

Example 1:
var loader: FXDLoader;
var fxdNode:fxsampleUI = fxsampleUI {
    url: "{__DIR__}fxsample.fxz"
    backgroundLoading: true
    placeholder: Text{ x:10 y: bind scene.height-50 content: "Loading graphics..." fill: Color.YELLOW}
    loader: loader = FXDLoader {
        onDone: function() {
            delete fxdNode.atom from fxdNode.getRoot().content;
            scene.content = fxdNode.atom;
        }
    }
}

NetBean generated fxsampleUI.fx:

import java.lang.\*;
import javafx.scene.Node;
import javafx.fxd.FXDNode;

public class fxsampleUI extends FXDNode {

    override public var url = "{__DIR__}fxsample.fxz";

    public-read protected var atom: Node;
    public-read protected var background: Node;

    override protected function contentLoaded() : Void {
        atom=getNode("atom");
        background=getNode("background");
    }

    /\*\*
     \* Check if some element with given id exists and write
     \* a warning if the element could not be found.
     \* The whole method can be removed if such warning is not required.
     \*/
    protected override function getObject( id:String) : Object {
        var obj = super.getObject(id);
        if ( obj == null) {
            System.err.println("WARNING: Element with id {id} not found in {url}");
        }
        return obj;
    }
}

To be continued...

Thursday Jun 18, 2009

A touching story for all Fathers.

This coming Sunday, 6/21 is Father's Day.
I know there is Father's Day. But it used to be just another day until I became to know this touching story.

http://www.youtube.com/watch?v=VJMbk9dtpdY

Monday Jun 15, 2009

Node Bounding Rectangles & Coordinates

A FX Node has 3 Bounding Rectangles. I think it is important to understand what they mean exactly, how each bounding rectangle is calculated and when they should be used.

layoutBounds

The rectangular bounds that should be used in calculations for both manual and automatic layout of the Node. This rectangle is used as a base layout information for a particular node. The actual layout position should be calculated after applying layoutX/layoutY on top of the layoutBounds. As a Node extended class may need its own special layout bounds, layoutBounds is defined as "public-read protected" while other 2 bounds (boundsInLocal and boundsInParent) are defined as "public-read" only. While a sub class of Node may have a different definition, the default includes only geometric bounds. 

+ geometric bounds(shape, non-zero strokes)

boundsInLocal

The rectangular bounds of this Node in the Node's untransformed local coordinate space. In this context, 'transform' includes transforms, layoutX, layoutY, translateX, translateY, scaleX, scaleY and rotate. So these transforms won't be included in the boundsInLocal. However the clip and effect are applied.

+ geometric bounds(shape, non-zero strokes)
+ clip
+ effect

boundsInParent

The rectangular bounds of the Node which includes its transforms Again in this context, 'transform' includes transforms, layoutX, layoutY, translateX, translateY, scaleX, scaleY and rotate. All these transforms are applied. So this bounding box is the surrounding box of what we actually see from the scene. Now the translation by "transforms: Translate{x: n}", "translateX: n", and "layoutX: n" has the same meaning in the calculation of the bounding box. The followings are applied in order.

+ boundsInLocal
+ transforms variable
+ layoutX/Y varibales
+ explicit transforms(translateX/Y, scaleX/Y, rotate)

All 3 bounding boxes are automatically recomputed whenever the geometry of a node changes. For this reason,

1) A variable that decides the geometry of a node should never be bound to any 3 bounding boxes. If so, java.lang.StackOverflowError will be thrown. For example, in the following code snippet, the boundsInLocal change will cause the x value change which will cause recomputation of boundsInLocal. At the end, StackOverflowError will be thrown.

var anode:Rectangle = Rectangle {
x: bind
anode.boundsInLocal.minX+anode.boundsInLocal.width\*0.5
    ...
}

2) A variable that decide a bounding box should never be bound to the same bounding box. This will also result in java.lang.StackOverflowError. For example, in the following code snippet, the  pivotX change will rescale and  recompute the boundsInParent, which will update the pivotX again. At the end, StackOverflowError will be thrown.

var anode:Path = Path {
    transforms: [
        Scale {
x: bind scaleSlider.value y: bind scaleSlider.value
pivotX: bind anode.boundsInParent.minX+anode.boundsInParent.width\*0.5 // java.lang.StackOverflowError
        }
    ]
    ...
}

Here is the little toy you can try out by yourself.

The source bundle can be downloaded from HERE.

There are 3 known issues on this demo and FX API description, which I had submitted at the time of this writing.

  1. http://javafx-jira.kenai.com/browse/RT-4954: Slider knob issue when value is between - min value and + max value.
  2. http://javafx-jira.kenai.com/browse/RT-4953: boundsInParent calculation issue when a clip/effect is set.
  3. http://javafx-jira.kenai.com/browse/RT-4951: Node class description is incorrect on the layoutBounds.


Friday Jun 12, 2009

MediaBox (1): Hello MediaBox!

JavaFX 1.2 SDK comes with the MediaBox sample. It can be tried out from the JavaFX samples website as well. It replaced the old SimleVideoPlayer sample and its dependent library, mediacomponent.jar (the source wasn't available in public) in JavaFX 1.1 release. MediaBox comes with fully opened sources and is currently used to play the JavaFX launching videos.

MediaBox is not just a sample that replaced the SimpleVideoPlayer. Here are some different usecases of MediaBox with JavaFX 1.2.

Media UI Controls

MediaBox has the following UI Controls that are extended from javafx.scene.control.Control. It would have more media controls over the time.

Basic UI Controls:

  • PlayControl: A toggle button with media play and pause.
  • MediaSlider: A media progress bar that has 2 time positions, one for the current time and other for the buffer time.
  • MediaTime: A configurable media time data label with either the elapsed time or the remaining time.
  • SpeakerControl: A toggle button that turns on and off speaker volume.
  • VolumeControl: A volume slider.
  • BufferIndicator: A spinning buffer indicator. It centers on the MediaScreen control.
  • MediaInfo: A media information pane that contains media title, description and media duration information. It rolls down when a mouse enter the MediaScreen control by default.
  • ErrorInfo: A media information pane that shows an error information when something failed. When it happens it overlaps on the top of the MediaScreen control.

Compound UI Controls:

  • MediaControlBar: A Compound UI Control that contains individual PlayControl, MediaSlider, MediaTime, SpeakerControl, VolumeControl and BufferIndicator controls.
  • MediaScreen: A Compound UI Control that contains MediaView node, MediaInfo and ErrorInfo controls.

MediaBox Node:

  • MediaBox: It's not a UI Control but simply a FX Scene Graph node that contains both MediaScreen and MediaControlBar compound UI Controls. 

Hello MediaBox!

The easiest way to use MediaBox can be illustrated in the following hello world example:

Stage {
    scene: Scene {
        content: MediaBox {
            mediaSourceURL: "http://a.server.com/your_media"
        }
    }
}

You will get this pre-fabricated media player with only a few lines:


I plan to write a few more series of MediaBox blogs.
See you later.


JavaFX 1.2 Async

In JavaFX 1.2, asynchronous operation support was improved. Let's take a brief look at the old approach before diving into the new approach. Here are some URLs that discussed old behavior of asynchronous operation since JavaFX 1.0.

As a summary of old behavior, there was a built-in support for the remote text document to download in a non EDT thread. But this was just one of asynchronous usecases and couldn't be used for many other usecases. In the above blog, James showed how-to get around for the non remote text download case. But the workaround is still cumbersome and forced users to use some implementation classes at com.sun.javafx.runtime.async package.

New javafx.async in JavaFX 1.2

 Now JavaFX 1.2 javafx.async package comes with much clean design to support various asynchronous usecases. Please note that only supported usecase RemoteTextDocument class was removed in 1.2. It's still simple enough to have only 3 new classes namely 2 FX abstract classes(JavaTaskBase/Task) and 1 Java interface(RunnableFuture).

In most of cases, users are required to create their own 2 classes.

  • A Java class(e.g. MyRunnable) that implements RunnableFuture interface. Simply complete the run() method that will be executed in a non-EDT thread by the JavaFX Asynchronous Framework. At this point of time, FX class that extends this interface is not accepted and may result in an exception thrown.
  • A FX class(e.g. MyTask) that extends JavaTaskBase. Simply override the create() method and create/return the RunnableFuture implemented class in the above step. 
Once you create MyTask you can call its start() and stop() functions to control the background work. There are 2 callback function variables(onStart/onDone) and various read-only status variables(started/stopped/failed/causeOfFailure/succeeded/done) that will be set from the non-EDT thread in a different point of time as it progresses.

Here is the UML sequence diagram to illustrate what's happening inside.


What about "progress" from the background thread?  

You will notice JavaTaskBase has the progress related variables such as maxProgress, progress and percentDone. How those are used when I want to be notified the progress? Well the current API doc didn't mention much about it. So hope this will be useful at the moment. Here is my take.

 Let's create a sample that can illustrate new JavaFX Asynchronous Framework with progress. This sample has 2 buttons(start/stop) and the count label at the bottom. The actual counting will happen in a non-EDT java thread simulating a background work. The start button will initiate the counting and the cancel button will stop the counting from the background thread. If the cancel button is not pressed, the counting will be done at 100. And the counted data will be shown in he FX UI as shown here.

async-figure3

 It's your Task responsibility to manipulate setting the maxProgress at the beginning and updating the progress variables as it goes in the run() method. One interesting point is that the maxProgress can be easily set from the FX MyTask probably either in create() or start() function. However the FX variable, progress should be set from the Java MyRunnable class. How can we do that when FX object can't be directly assessed in a Java object?

 The trick is to create a Java interface called Progressable. Let MyTask implement Progressable interface as well.  Pass itself to the java MyRunnable as a Progressable, which is a java interface. The  Progressable interface has only one method, setProgress(val). MyRunnable java class should now be able to call Progressable.setProgress(val) as it goes.

Here is the Progressable interface:


public interface Progressable {
    void setProgress(long progress);
}

 Unfortunately this interface is not a part of javafx.async package. Please copy and paste it to your project source dir. Please note that MyTask is now implementing both JavaTaskBase and Progressable. The percentDone is automatically calculated when the progress is updated. 

 Here is the MyRunnable java class:

import javafx.async.RunnableFuture;
import com.sun.javafx.functions.Function0;

public class MyRunnable implements RunnableFuture {
    CountData counter;
    Progressable task;

    public MyRunnable(CountData cdata, Progressable pi) {
        counter = cdata;
        task = pi;
    }

    @Override
    public void run() throws Exception {
        while(counter.count < 100) {
            counter.count++;
            setProgress(counter.count);
            Thread.sleep(100);
        }
    }

    void setProgress(final long progress) {
        javafx.lang.FX.deferAction(new Function0<Void>() {
            @Override
            public Void invoke() {
                task.setProgress(progress);
                return null;
            }
        });
    }
}

 As all events should be dispatched in the EDT, FX.deferAction() has to be used to invoke MyTask.setProgress(progress). When FX.deferAction() is used, the passed function will be invoked from the EDT. 

 Here is the MyTask FX class:

import javafx.async.\*;

public class MyTask extends JavaTaskBase, Progressable {
    public-init var countData: CountData;
    var peer: MyRunnable;

    override function create(): RunnableFuture {
        peer = new MyRunnable(countData, this);
    }

    override function setProgress(pro: Long) {
        progress = pro;
    }

    override public function start():Void {
        maxProgress = 100;
        progress = 0;
        super.start();
    }
}

 Here is another diagram to illustrate what's happening inside.

async-figure2

 Finally the illustrated sample NetBean project can be downloaded from here (JavaAsyncSample).

There are a couple of things worthwhile to mention at last. 

  1. Tasks are expected to be executed one time (one call to start()  per instance of a Task).
  2. JavaFX code should not be run from objects that support RunnableFuture interface. Some related discussion if you are interested in.

Have a fun with a threading in JavaFX 1.2.



About

Hello,
I am Baechul and live in Santa Clara, U.S.A. This is my blog that will talk about Java and JavaFX technologies.

Search

Top Tags
Categories
Archives
« April 2014
SunMonTueWedThuFriSat
  
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
   
       
Today