Skip to main content

Loading Image

5 replies [Last post]
riepi
Offline
Joined: 2008-03-27
Points: 0

Hi,

i am developing a javafx program which gets some data from a database. Works fine, but when i call the function which returns the data everything else in my JavaFx programm ist stopping until all data are loaded. So its not possible to do some animation during this time. Would be nice to build an Loading image/grafic (a Class which extends Customnode with some kind of animation) which doesn't stop while loading the data. The other idea to show only an animated loading-GIF picture did not work, because Animation of Gif seems not to be supportet.

Anybody an idea how to realize this in JavaFX? I am not so familiar with Java, but what about using Threads?

Best Regards Riepi

Reply viewing options

Select your preferred way to display the comments and click "Save settings" to activate your changes.
rperfect
Offline
Joined: 2005-07-12
Points: 0

My understanding is that JavaFX still operates ontop of the Java AWT just like Swing does - which yes does make it "single-threaded" and yes, that is common across most UI frameworks (Win32 API included as I understand).

Creating threads in JavaFX is fine but I think what Michael really meant to say is that from a non event dispatch thread you shouldn't go changing the state of objects that are displayed on the UI because the UI framework might be in the middle of rendering you UI component and you don't want it to pick-up some changes and not others.

JavaFX includes a class javafx.lang.DeferredTask which allows Threaded actions to sync back in with the UI framework but it doesn't provide any help on starting up a thread to perform the long running task of downloading the image. It just means when you're ready it can add the image back into the UI in a Thread safe manner.

The common pattern which is used in Swing and which should still be just as usable here in JavaFX is to use SwingWorker.

http://java.sun.com/javase/6/docs/api/javax/swing/SwingWorker.html

You should be able to use SwingWorker pretty much out of the box because even though it's Swing and not JavaFX really the class is a utility class that is interacting with the underlying AWT event dispatch thread.

In the API doc it also mentions a reference to a tutorial on how to use the class

http://java.sun.com/docs/books/tutorial/uiswing/concurrency/index.html

I have to do this sooner or later on my project so I might have a stab at it tonight, no promises though :-)

rperfect
Offline
Joined: 2005-07-12
Points: 0

Okay so I've got an example working using the Swing Worker and even figured out how to get call from Java back into JavaFX, which I found in one of Michael Heinrichs blogs (Thanks! .... http://blogs.sun.com/michaelheinrichs/entry/using_javafx_objects_in_java)

NOTE: the AbstractSyncOperation is probably a more JavaFX-ey way of doing this but although it's in the javafxrt.jar it's not documented in the JavaFX docs. It's probably coming out in the 1.0 release next month.

What I've done is modified the "Bouncy Bubbles" example so that if you click on a Bubble the onMouseClicked() function blocks and waits five seconds and then changes the color of the Bubble to pink.

If you uncomment the following code then the whole UI freezes and simulates the problem you are having.

[code]
// Option 1: Long running action executes on event dispatch thread
try {
Thread.sleep(5000);
color = Color.PINK;
}
catch(ex : InterruptedException ) {
// wake up and carry on
}
[/code]

But if you uncomment the following code the program still waits five second before changing the color of the bubble but it doesn't freeze the whole UI.

[code]
// Option 2: Long running action executed in it's own thread
var worker = new ColorMePinkWorker(this);
worker.execute();
[/code]

How this works is that ColorMePinkWorker subclasses the SwingWorker class and when you call execute() it starts a new thread to perform your action inside of the doInBackground() method. When this method finishes the SwingWorker places itself on the Event Dispatch Queue and waits for the right moment to call the done() method which makes the UI color change.

The rest of the code follows....

- Richard.

[code]
/*
* Copyright (c) 2007, Sun Microsystems, Inc.
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions are met:
*
* * Redistributions of source code must retain the above copyright notice,
* this list of conditions and the following disclaimer.
* * Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in
* the documentation and/or other materials provided with the distribution.
* * Neither the name of Sun Microsystems, Inc. nor the names of its
* contributors may be used to endorse or promote products derived
* from this software without specific prior written permission.
*
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
* "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
* LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
* A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
* OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
* SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED
* TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
* PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
* LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
* NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
* SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/

package motion;

import javafx.scene.Node;
import javafx.scene.CustomNode;
import javafx.scene.geometry.Circle;
import javafx.scene.paint.Color;
import javafx.application.Frame;
import javafx.application.Stage;
import javafx.animation.Timeline;
import javafx.animation.KeyFrame;

import javafx.input.*;

import java.lang.*;
import java.util.Random;

/**
* @author Michal Skvor
*/

var spring : Number = 0.05;
var gravity : Number = 0.05;

var bubbles : Bubble[];

var width : Number = 200;
var height : Number = 200;

var timer : Timeline = Timeline {
repeatCount: Timeline.INDEFINITE
keyFrames :
KeyFrame {
time : 16ms
action : function() : Void {
for( bubble in bubbles ) {
bubble.collide( bubbles, spring, width, height );
bubble.move( gravity, width, height );
}
}
}
};
var rnd : Random = new Random();

for( i in [1..12] ) {
insert Bubble {
x : rnd.nextInt( width ), y : rnd.nextInt( height ), radius : rnd.nextInt( 10 ) + 10
color : Color.WHITE, opacity : 0.8
} into bubbles;
}

Frame {
stage : Stage {
fill : Color.GRAY
content : bind bubbles
};

visible : true
title : "Bouncy Bubbles"
width : 200
height : 232
closeAction : function() { java.lang.System.exit( 0 ); }
}

timer.start();

public class Bubble extends CustomNode, BubbleInterface {

public attribute x : Number;
public attribute y : Number;
public attribute radius : Number;
public attribute color : Color = Color.WHITE;

public attribute vx : Number;
public attribute vy : Number;

override attribute onMouseClicked = function(event: MouseEvent) : Void {

// Option 1: Long running action executes on event dispatch thread
try {
Thread.sleep(5000);
color = Color.PINK;
}
catch(ex : InterruptedException ) {
// wake up and carry on
}

// Option 2: Long running action executed in it's own thread
//var worker = new ColorMePinkWorker(this);
//worker.execute();

}

public function setColor() {
color = Color.PINK;
}

public function collide( bubbles : Bubble[], spring : Number, width : Number, height : Number ): Void {
for( bubble in bubbles ) {
var dx : Number = bubble.x - x;
var dy : Number = bubble.y - y;

var distance : Number = Math.sqrt( dx * dx + dy * dy );
var minDist : Number = bubble.radius + radius;

if( distance < minDist ) {
var angle : Number = Math.atan2( dy, dx );
var tx : Number = x + Math.cos( angle ) * minDist;
var ty : Number = y + Math.sin( angle ) * minDist;

var ax : Number = ( tx - bubble.x ) * spring;
var ay : Number = ( ty - bubble.y ) * spring;

vx -= ax;
vy -= ay;

bubble.vx += ax;
bubble.vy += ay;
}
}
}

public function move( gravity : Number, width : Number, height : Number ): Void {
vy += gravity;
x += vx;
y += vy;

if( x + radius > 200 ) {
x = width - radius;
vx *= - 0.9;
} else if( x - radius < 0 ) {
x = radius;
vx *= 0.9;
}
if( y + radius > 200 ) {
y = height - radius;
vy *= -0.9;
} else if( y - radius < 0 ) {
y = radius;
vy *= -0.9;
}
}

public function create(): Node {
return Circle {
centerX : bind x, centerY : bind y, radius : bind radius
fill : bind color
};
}
}
[/code]

[code]
package motion;

public interface BubbleInterface {

public void setColor();

}

[/code]

[code]
package motion;

import javax.swing.SwingWorker;

public class ColorMePinkWorker extends SwingWorker {

private BubbleInterface someBubble;

public ColorMePinkWorker(BubbleInterface someBubble) {
this.someBubble = someBubble;
}

@Override
public String doInBackground() {
// This method is called in a Thread created just for this class and
// outside of the event dispatch thread.
try {
Thread.sleep(5000);

// So... DON'T set the color here - because screen might be being redrawn
//someBubble.setColor();
}
catch(InterruptedException ex) {
// wake up and carry on
}

return null;
}

@Override
public void done() {
// Set the color here, because "now" is a threadsafe time to do so.
// This method is called from the event dispatch thread at a time when
// the UI framework is processing user actions and not redrawing the
// the screen.
someBubble.setColor();
}
}

[/code]

riepi
Offline
Joined: 2008-03-27
Points: 0

Thank you very much. I think this will help me. Also found in WidgetFX the class JavaFXWorker which extends AbstractAsyncOperation (http://widgetfx.googlecode.com/svn-history/r148/trunk/api/src/org/widget...). Works quite fine.

Riepi

haraldk
Offline
Joined: 2005-05-10
Points: 0

Hi Riepi,

Not really a JavaFX guy, but the problem you have come across reaches across most UI frameworks because of their single-threaded nature: When you start to perform long-running operations (anything above 100 ms is long-running in this context) in the UI thread, the application will become unresponsive. Unfortunately, showing a progress indicator or animated GIF will not help you here, because they will also need the UI thread to update themselves.

The proper way is to read the data asynchronously. Not sure how to do that in JavaFX, but the checking out the AbstractAsyncOperation mentioned by Michael, sounds like a good place to start.

Regards,

.k

michael_heinrichs
Offline
Joined: 2007-11-15
Points: 0

The Image class is able to load its content in the background. It requires a URL to access the resource though, so I don't know, if this works in your case.

Another option might be to extend AbstractAsyncOperation, but that is clearly a more advanced topic.

JavaFX is a single-threaded language and you shouldn't create any new threads yourself.