So last time we left off with an admittedly boring Dial. This time I am going to show you how to be able to programmatically manipulate this
SVG Dial you just created. Why would you want to be able to do that? Well in line with the essence of Object Oriented Programming, it sure would be convenient to just draw one Dial and create as many different variations of it as you need in your application. Or if you want to create themes you can simply just create new instances with different fill colors (or any alterations your interested in).
So all we have to do is follow the instructions from the JavaFX Production Suite
Well not exactly. If you are simply taking your completed image, and just want to plop it, as is, into your program then no worries. What I found is that the sample UIStub class that NetBeans created was, well exactly that, a sample. Thus I am going to deliver two warnings, where you have to be cognizant, or the CustomNode produced will be surprisingly the same as the image itself. It took me quite some time to figure out the “right” way to implement the UIStub, in order to create accessible member variables that could update the image. Believe it or not, it really isn’t as straight forward as I would have thought.
Where to begin
You must have the JavaFX 1.1 SDK downloaded and installed (I would assume you have already accomplished this feat if you’re viewing this article). Next download and install the JavaFX Production Suite, if you haven’t done so already. The JavaFX Production Suite, provides a conversion utility to go from SVG to JavaFX’s fxz, (JavaFX compressed file format). There are also plugins for the Adobe Photoshop, and Illustrator products. If you’ve followed the drawing process from the last article you should have a SVG drawing that can convert, and import into NetBeans. First we need to run the conversion utility on the javafx_dial.svg we created. Located in the .\JavaFX Production Suite\SVG Converter directory you will have the SVG Converter.exe tool (admittedly I haven’t had the chance to try using this utility in Linux, but it looks like the exe may be just a wrapper for a Java application). Execute the program, and enter the location of the javafx_dial.svg file, and then where you want the output javafx_dial.fxz file to be placed. Note: The option checkbox, “Preserve “JFX:” IDs only, is by default checked. Uncheck this box. This option is so that you can have Graphics converter only import the objects that you have given the label of JFX:xxxx, which is great if you have successfully labelled all of the parts of the image that you are going to need. As for the javafx_dial, none of the items are in fact labeled this way, and there will be Paths that you will need to use the default label (this is a cheap chance to point out in NetBeans the ability to dissect the javafx_dial.fxz down to its declarations). Now that we have the javafx_dial.fxz file created you now need to open your IDE.
Working with your IDE
Open your favorite IDE, as for myself, I have since been working with NetBeans, but you can use Eclipse , jEdit, etc.. For reference this article examines
the specifics of NetBeans. With NetBeans open, create a new JavaFX project (I am assuming that the JavaFX plugins have been installed, if not try here). Name the project something meaningful to you, for this article my project name is JavaFXDial, and the Main File is called JavaFXDial.
Working with your JavaFX_Dial 
Using your File explorer, Drag and Drop your new javafx_dial.fxz on to the “Source Packages” area under your project. NetBeans includes a viewer to the fxz file format. If you double click on the javafx_dial.fxz file, you should see it in the “Navigator” panel. This will allow you to move through the XML style format, and as you click you will see the individual Groups, and SVGPaths become highlighted with a red outline. This becomes more useful when you begin looking to manipulate certain aspects of your drawing, that you either forgot to tag, or when the drawing hasn’t been tagged.
Creating the UIStub
Right click on the javafx_dial.fxz file under the package name (under Source packages), and select “Generate UI Stub…”. Select the default UIStub location, and hit OK. We have successfully followed the instructions provided in the UIStub example. This doesn’t get us exactly where we need to be.
public class javafx_testlUI extends UiStub
{
override public var url = “{__DIR__}javafx_dial.fxz”;
public var bottom: Node;
public var layer1: Node;
public var path2696: Node;
public var path2702: Node;
public var path3656: Node;
public var path4194: Node;
public var top: Node;
override protected function update()
{
lastNodeId = null;
try
{
bottom=getNode(”bottom”);
layer1=getNode(”layer1″);
path2696=getNode(”path2696″);
path2702=getNode(”path2702″);
path3656=getNode(”path3656″);
path4194=getNode(”path4194″);
top=getNode(”top”);
}
catch( e:java.lang.Exception)
{
System.err.println(”Update of the attribute
’{lastNodeId}’ failed with: {e}”);
throw e;
}
}
}
The problem is this class doesn’t seem to really understand our drawing.
Warning Number 1.
The NetBeans code generator apparently scours the javafx_dial.jfx file for anything with an ID tag and assigns it a name based on the name of the Node. The issue here is everything in the file isn’t a node, there are groups, and SVGPaths. Hence, you can either cast, or if there is an applicable member function, you can just import the Node correctly (I am taking the same stance here by calling it a Node, because most of the objects inherit from the Node Class, that’s why casting a Node to a Group for example isn’t really an issue). So if you look at the API for the FXDContent class, which is used to import from the image, you will see the appropriate get functions for the object you need. To verify the object that you need you can always go back to the “Navigator” Panel, after double clicking on the javafx_dial.fxz, and walk through the Nodes. This is how I corrected the imports (including adding more meaningful names), notice of couse I changed the object’s declaration to match the Node type I was importing:
public class javafx_testlUI extends UiStub
{
override public var url = “{__DIR__}javafx_dial.fxz”;
public var top: Group;
public var bottom: Group;
public var bottomCircle: SVGPath;
public var sawToothPattern: SVGPath;
public var pointer: SVGPath;
public var topCircle: SVGPath;
override protected function update()
{
lastNodeId = null;
try
{
bottom=
getGroup(”bottom”);
bottomCircle=
getNode(”path2696″) as SVGPath;
sawToothPattern=
getNode(”path2702″) as SVGPath;
pointer=
getNode(”path3656″) as SVGPath;
topCircle=
getNode(”path4194″) as SVGPath;
top=
getGroup(”top”);
}
catch (e:java.lang.Exception)
{
System.err.println(”Update of the attribute
’{lastNodeId}’ failed with: {e}”);
throw e;
}
}
}
Notice just for sake of clarity, I did cast the getNode(…) functions to the SVGPath, because there was no associated
getSVGPath function.
Altering the Dial
Well at this point you could in fact add to the Update function below the getNode(..) calls to set the Fill, such as:
pointer.fill = Color.YELLOW;
topCircle.fill = Color.GREEN;
bottomCircle.fill = Color.PINK;
// Actually this can be any Paint object but for sake of
// brevity, I just used Colors.
Well that isn’t too dynamic, or really re-usable. So what I was thinking was well, just turn the assignment into a variable and your good to go.
Hence warning number 2.
You can’t just assign pointer.fill = mySuperFabulousVariable, and expect anything to happen, when you assign it in your main program. There is an order of events with instantiating the UIStub, it really requires you to delete the built-in line:
override public var url = "{__DIR__}javafx_dial.fxz";
Instead you need to create an init function, and as the last, last, very last thing you do in this init is to assign that URL variable to your .fxz file location. The reasoning is that any assignments you perform before that will not take effect. So in my example I want to change four elements from my main program that is going to be instantiating my class: Top of the dial, bottom of the dial, pointer, and the sawtooth pattern. This is how I was able to access, and modify those variables:
init
{
// If the topColor isn’t assigned use Default.
if (topColor == null)
{
topColor = RadialGradient
{
centerX: 0.45;
centerY: 0.5;
stops:
[
Stop
{
offset: 0.0
color: Color.WHITE
},
Stop
{
offset: 0.4,
color: Color.DIMGRAY
},
Stop
{
offset: 0.7
color: Color.BLACK
},
]
};
}
// If the pointerColor isn’t assigned use Default.
if (pointerColor == null)
{
pointerColor = LinearGradient
{
startX: 0.0,
startY: 0.0,
endX: 1.0,
endY: 0.0
proportional: true
stops:
[
Stop
{
offset: 0.0
color: Color.LIGHTGRAY
},
Stop
{
offset: 0.4,
color: Color.LIGHTGRAY
},
Stop
{
offset: 0.45
color: Color.WHITE
},
Stop
{
offset: 0.5
color: Color.LIGHTGRAY
},
Stop
{
offset: 0.6
color: Color.DIMGRAY
}
Stop
{
offset: 1.0
color: Color.BLACK
},
]
}
}
// If the bottomColor isn’t assigned use Default.
if (bottomColor == null)
{
bottomColor = RadialGradient
{
centerX: 0.45;
centerY: 0.5;
radius: 0.5;
stops:
[
Stop
{
offset: 0.0
color: Color.WHITE
},
Stop
{
offset: 0.4
color: Color.LIGHTGRAY
},
Stop
{
offset: 0.7
color: Color.LIGHTGRAY
},
Stop
{
offset: 0.9
color: Color.DIMGRAY
},
Stop
{
offset: 1
color: Color.BLACK
}
]
}
}
// If the sawToothColor isn’t assigned use Default.
if (sawToothColor == null)
sawToothColor = Color.DARKGREY;
/* So after a lot of testing I found that you MUST put
* the URL assignment last, or the default constructors will not
* work. I don’t really know why that is.
*/
url = “{__DIR__}javafx_dial.fxz”;
}
Then in the update function I added at the bottom:
/*
* Here is the actual Fill assignments being made. If the value isn’t
* set then the Default from the INIT is used.
*/
pointer.fill = pointerColor;
topCircle.fill = topColor;
bottomCircle.fill = bottomColor;
Coloring the Sawtooth Pattern
Alright so the Sawtooth was more difficult to fill then I had foresight for. When I repeated the ‘U’ shape around the path of the circle, there was an inherent problem born. When you got to fill that ‘U’ Shape you end upd with half of a colored Arc. Well this isn’t very useful at all. It leave the bottom of the ‘U’ shape completely empty, until it hits the circle part of the object. Thankfully, this is JavaFX. So I created a new Circle around this new problem, which is really the size of the Bottom grouping (remember the Bottom group was the Sawtooth and the Circle). Then I created a new Shape, (actually a ShapeSubtract object) which was the remnants by removing both the sawToothPattern and the bottomCircle from this new Circle. Next I filled this new Shape, and added it to the Bottom Group.
var myCircle = Circle
{
centerX: bottom.boundsInLocal.minX + (bottom.boundsInLocal.width / 2)
centerY: bottom.boundsInLocal.minY + (bottom.boundsInLocal.height / 2)
// The -5 is used to get rid to the extra halo that gets created by the Circle
radius: bottom.boundsInLocal.width / 2 - 5
}
var sawTooth = ShapeSubtract
{
a: myCircle
b:
[
sawToothPattern,
bottomCircle
]
}
/*
* sawToothColor has a default assignment in the INIT, but can be
* overwritten.
*/
sawTooth.fill = sawToothColor;
// Add the new sawTooth instance to the Bottom Group.
insert sawTooth before bottom.content[0];
Running my own Dial
Now that we have fixed this, go back to your main, and instantiate this javafx_dialUI object, you can now overwrite those Paint objects (topColor, pointerColor, bottomColor, and sawToothColor), or keep just the defaults.
Stage
{
title: “JavaFx Dial Example”
width: 165
height: 200
scene: Scene
{
fill: Color.WHITE
content: Group
{
scaleX: 0.5
scaleY: 0.5
translateX: -225
translateY: -125
content: javafx_dialUI
{
// Go ahead overwrite the fill of the Object.
/*
* Or you can take the Defaults.
* topColor: Color.ORANGE
* bottomColor: Color.GREEN
* sawToothColor: Color.YELLOW
* pointerColor: Color.PINK
*/
}
}
}
}
For the NetBeans Project, the Java files, or for the SVG Dial just click.
In my next article, I plan to actually make the Dial, well into a dial that has both animation, and function.
< previous Part 1. Turning an SVG drawing into a JavaFX Dial
Part 3. Breathing life into our JavaFX Dial next >
- - tekgnu - -
tekgnu Programming Dial, fxz, JavaFX, Tutorial, UiStub