Last year I blogged about a small JavaFX project I implemented called Folder-Visualizer. The tool lets you find large files and directories to effectively clean your hard disk. The previous version of the tool was implemented with an early beta of JavaFX. I reimplemented it with JavaFX 1.2, now using standard components, charts and background tasks. I will show some of the code to give an idea what it takes to implement a real application with the current version of JavaFX.
Charts
In a previous post I blogged about the new charting components in JavaFX 1.2. They are easy to use. Common functionality such as rendering from a sequence of data and clicking on a part of the chart is all supported, and they look ok too. In the PieChart there is a very annoying rendering error however (the black line at the left top) which is always there. And while I think the charts look good, they are nowhere near the looks of the Flex charting components yet...
Temporarily adding nodes
Sometimes you want to add a node temporarily. For example, when this app is indexing, the chart is removed and a ProgressIndicator is displayed. As soon as the indexing process is finished, the ProgessIndicator is removed again and the new chart is added to the scene. The easiest way to remove a node is by using it's id. You can give a node an id by setting the id property, which is a String. Next you can use the "lookup" function on Scene. This is very much like the "getElementById" method in JavaScript, and you can use it the same way. So to remove the chart (id: "chart") you can use the following code:
delete scene.lookup("chart") from scene.content;
Also remember that the scene's content is just a sequence. This means you can easily add nodes on the fly too, for example:
insert ProgressIndicator {
id: "progress"
progress: -1;
translateX: 300
translateY: 400
scaleX: 10
scaleY: 10
} into scene.content;
Binding to Java
The actual scanning and indexing of directories is implemented in Groovy. The Groovy classes are simply packaged in a JAR file, and the JAR file is used from JavaFX. JavaFX can call any Groovy code without a problem, but some extra work was still necessary. The Groovy classes return a java.util.List with the indexing information. JavaFX can use lists, but you can't use binding on it. The following code for example will not compile:
var list = new ArrayList();
list.add("Str1");
list.add("Str2");
var seq = bind for(item in list) {
item;
}
This can be partly solved by transforming the List into a Sequence yourself (example below) so you can at least use binding and sequence syntax in the rest of the code, but you still need to update the sequence yourself when the List is changed.
function createSequence(list : List) : FolderItem[]{
var items : FolderItem[] = [];
for(item in list) {
insert item as FolderItem into items;
};
return items;
}
While this might look like a problem, it's just a inconvenience. It's great that JavaFX call Java. This feature alone makes JavaFX more interesting than AIR, where your whole app must be ActionScript. While I don't mind writing the UI in a new (less mature) language, I don't want to write all my code in it.
Strange binding bug?
I did run into some strange error at some point. As you might have noticed in the code of the BreadCrum, I use a bind for color while this is already in a bound context. To me this seems unnecessary. However if I remove the bind, the application still works correctly, but also shows a warning at the command line, telling that a node was added to a Group while not removed from it's old Group. I can't explain it at this moment, and to me it seems like a bug.
Talking about bugs, I found that a lot of bugs are fixed in version 1.2. In previous versions I did still sometimes run into, often binding related, bugs. Besides this error everything worked exactly as expected.
Using FileChooser
JavaFX doesn't have a FileChooser component. You can use the Swing JFileChooser without any problem however, and it doesn't compromise rendering since a native window is used anyway.
var fc = new JFileChooser();
fc.setFileSelectionMode(JFileChooser.DIRECTORIES_ONLY);
var retVal = fc.showOpenDialog(null);
if(retVal == JFileChooser.APPROVE_OPTION) {
dirNameInput.text = fc.getSelectedFile().getAbsolutePath();
}
Executing background tasks
Another new feature in JavaFX 1.2 is support for background task execution. In the Folder-Visualizer application the indexing can potentially take a long time, and you don't want the interface to freeze as soon as indexing starts. The indexing should run in a separate thread obviously. JavaFX has the JavaTaskBase class that is similar with Java's Callable. You need to override the "create" function that returns a RunnableFeature. The RunnableFeature contains the real work, and can be implemented in Java. When the task is done, you probably want to change something in the UI. Similar to Swing programming you can use a method "Entry.deferAction(Runnable)" to run code on the UI thread. The code that will run on the UI thread is a callback.
Confused yet? It took me a minute to grasp it. Three extra classes to just execute a piece of Java code on the background. While it's an elegant solution, it's still feels like a lot of work. So to summarize you'll need the following
- A task description (written in JavaFX, extends JavaTaskBase)
- A RunnableFuture implementation (written in Java, implements RunnableFuture)
- A callback interface (so that Java can call JavaFX)
I will show the implementation of each class, and the code that actually starts the task.
Task Description
public class VisualizeTask extends JavaTaskBase{
public var dir : String;
public var callback : FXListener;
public override function create() {
new VisualizeFuture(dir, callback);
}
}
RunnableFuture implementation
public class VisualizeFuture implements RunnableFuture {
private final FXListener listener;
private final String dir;
public VisualizeFuture(final String dir, final FXListener listener) {
this.listener = listener;
this.dir = dir;
}
public void run() throws Exception {
Folder folder = new Folder(dir);
FolderVisualizer visualizer = FolderVisualizer.create(folder);
final List<FolderItem> items = visualizer.toVisualize(folder, 5);
Entry.deferAction(new Runnable() {
public void run() {
listener.callback(items);
}
});
}
}
Callback interface
public interface FXListener {
public void callback(List<FolderItem> items);
}
Executing the task
var visualizeTask = VisualizeTask {
dir: dirNameInput.text
callback: FXListener {
override function callback(ret : List) : Void{
createChart(createSequence(ret));
}
}
};
Downloading Folder-Visualizer and check the code
The Folder-Visualizer is hosted at Google Code: http://folder-visualizer.googlecode.com. You can install the application by simply clicking the JNLP file, it will install as Webstart application. Besides the JavaFX version, there is a AIR version developed by Jamie Craane.
Posted
17-08-2009 16:22
by
Paulb