torsdag 8. november 2012

JavaFX - FXML ... "Root value already specified."

"Curiosity killed the cat you know". Tim Burtons fantastic movie Nightmare Before Christmas, and this quote from Doctor Finklestein comes to mind. The movie is about Jack Skellington, the Pumpkin King of Halloween Town, who discovers Christmas. And what a mess that becomes, since he doesn't quite understand what Christmas is.

Trying to be clever usually gives me a lot of hassle, so I guess I can relate to this Jack Skellington character.

Here I am, trying to create a nifty JavaFX CSS Demo. But why choose the easy path? "I should try do this with FXML which I haven't looked at yet". It had to go wrong right?

Have you tried Google this?
Caused by: javafx.fxml.LoadException: Root value already specified.

I guess I might be the first to try explain this on the entire internett ... *sigh*


My idea was to use the JavaFX Scene Builder to create a simple user interface, and save it as FXML. Then use the FXML file in my Java code so I didn't have to create all the layout, labels, buttons and so forth with code. I wanted to try something new for once, something that might be close to how JavaFX should be used.

I did a quick run in JavaFX Scene Builder. Added a Gridpane, a few labels, textfield, and the default button which every form must have.


There. Done!!
...

So I found this link to Oracle officially tutorial on FXML from Google.
http://docs.oracle.com/javafx/2/fxml_get_started/custom_control.htm#BABDAAHE

"Mastering FXML". Nice.

Then I do what every proper heterosexual man would have done: Don't read the text, just copy/paste the code, and try compile the result. You don't have time for reading while working, why start now in your spare time?

Yeah. BOOM baby. Yeah! Big bada boom!

Caused by: javafx.fxml.LoadException: Root value already specified.
 at javafx.fxml.FXMLLoader.createElement(FXMLLoader.java:2362)
 at javafx.fxml.FXMLLoader.processStartElement(FXMLLoader.java:2311)
 at javafx.fxml.FXMLLoader.load(FXMLLoader.java:2131)
 at javafx.fxml.FXMLLoader.load(FXMLLoader.java:2028)

Nice heh?
Since Google returned NADA, this meant another happy evening with coding got ruined trying to figure out what went wrong.

Ok! The solution...

The tutorial wanted me to use this FXML code.

<fx:root type="javafx.scene.layout.VBox" xmlns:fx="http://javafx.com/fxml">
    <textfield fx:id="textField">
    <button onaction="#doSomething" text="Click Me">
</button></textfield></fx:root>

But, clever as I am, I used my own layout instead of the daft example from the tutorial. Again, why read the tutorial - Sun Oracle has always been intuitive right?

<AnchorPane id="AnchorPane" prefHeight="118.0" prefWidth="380.0" xmlns:fx="http://javafx.com/fxml">
  <children>
    <GridPane hgap="5.0" layoutX="14.0" layoutY="8.0" vgap="5.0">
... snipp ...
    </GridPane>
  </children>
</AnchorPane>

Do you see the problem now? The annoying part is that this is described in the tutorial. You have to change, by manual hand work, from <AnchorPane> to <fx:root>.

Long story made even longer

There is an explanation to this.
The old way is to declare the exact controller you are going to use in the FXML file, which you can easily do in the Scene Builder. Then when you load the FXML, it also load and init the controller, and you have to ask the FXMLLoader for the controller.
This is not side effect free. Especially if you love structuring code and have ideas.
zenjava and Dan Zwolenski (zonski) have explored MVP and JavaFX quite extensively, and he gives a lot of pro/cons to various techniques.

In JavaFX 2.2(aug.2012), a new feature was added to the FXML, which is the <fx:root> tag. With this you don't declare the controller in the FXML file. You do have to define the type of the root element, create a class which extend the expected type, and give that reference to the FXMLLoader. You can now create your own controller, and have the FXMLLoader to inject resources into that one. I guess it is an attempt to decouple the fxml and the controller.
When I saw the code from the "Mastering FXML" tutorial, I thought it looked good. It was simple and straight forward. No fancy fuzz. I thought...

When you give an FXML file, with the AnchorPane as top element, to the FXMLLoader where you have defined some other Nodes to be the root and controller - you will get "Root value already specified.".

A smelly wet cat

Now that the smelly wet cat is on the table, I'm not sure if I like any of this.
The JavaFX Scene Builder does not yet support this feature. I guess it will, but I hate the idea of subtle features like this. Do you want an AnchorPane with a named controller, or do you want a root with a distinct node type?
I don't want to learn FXML syntax, or debug it. It is perfectly ok to have a format, but don't bother me with it.

IT IS 2012 - WHY CAN'T IT JUST WORK????

- oh, you forgot to use notepad and swap AnchorPane with root... yeah I know ... notepad...
- oh, encoding issues ... oh, you didn't knew saving this as ascii would screw things up?
- oh, typos...
- oh, no you can't define a controller in root...
- oh, you don't understand the difference between a controller and VBox?
- oh, typos... VBox, not HBox...
- oh, no this is not xml, it does not need an closing tag...
- oh, you thought you were clever now? sry, no support for that I'm afraid....
- oh, you just reloaded the fxml file, and forgot to re-add the xxxx?
- oh, you thought you could change the fxml file yourself? The reason we got a dedicated fxml team is to avoid wasting time on these stupid mistakes.
- oh, you didn't read ...
- 0-900-FXML-HELP how can I help you? mhm mhm. This is gonna take a while...*sigh*


I just want to get a random FXML file, and load it. Oracle, have you tested this with random Java programers? Usability testing? Just hire a bunch of coding prima donas and pattern lovers. They would have pointed it out ages ago.

But having to live with, and be restricted by, the mistakes from the past - is just how Java is.... *sigh*

I made it work in the end. The code is found in the CSS Reload example on my github:
https://github.com/frtj/javafx_examples
JavaFX, MVP and notes about FXML
http://www.zenjava.com/2011/12/11/javafx-and-mvp-a-smorgasbord-of-design-patterns/
The Official FXML tutorial using custom controller and <fx:root>
http://docs.oracle.com/javafx/2/fxml_get_started/fxml_tutorial_intermediate.htm#sthref21
The Official old fashion FXML tutorial
http://docs.oracle.com/javafx/2/get_started/fxml_tutorial.htm
A kind of reference for FXML
http://docs.oracle.com/javafx/2/api/javafx/fxml/doc-files/introduction_to_fxml.html

v1.0 - 06.nov.2012

Ingen kommentarer:

Legg inn en kommentar