I want to program a "Simple UML Editor"
Use Case
Click UML canvas, and generate a UML shape. Cursor is at the UML shape's top left after generated. As this image.
Here is sample code.
package application;
import javafx.application.Application;
import javafx.fxml.FXMLLoader;
import javafx.scene.Parent;
import javafx.scene.Scene;
import javafx.stage.Stage;
public class Main extends Application {
@Override
public void start(Stage primaryStage) {
try {
Parent root = FXMLLoader.load(getClass().getResource("mainView.fxml"));
Scene scene = new Scene(root);
primaryStage.setScene(scene);
primaryStage.show();
} catch(Exception e) {
e.printStackTrace();
}
}
public static void main(String[] args) {
launch(args);
}
}
<?xml version="1.0" encoding="UTF-8"?>
<?import java.lang.*?>
<?import javafx.scene.layout.*?>
<Pane fx:id="canvas" maxHeight="-Infinity" maxWidth="-Infinity" minHeight="-Infinity" minWidth="-Infinity" onMouseClicked="#onMouseClicked" prefHeight="400.0" prefWidth="600.0" xmlns="http://javafx.com/javafx/8" xmlns:fx="http://javafx.com/fxml/1" fx:controller="application.MainViewController" />
package application;
import javafx.fxml.FXML;
import javafx.scene.input.MouseEvent;
import javafx.scene.layout.Pane;
public class MainViewController {
@FXML Pane canvas;
@FXML private void onMouseClicked(MouseEvent event) {
myCircle c = new myCircle();
c.setLayoutX(event.getX());
c.setLayoutY(event.getY());
canvas.getChildren().add(c);
}
}
<?xml version="1.0" encoding="UTF-8"?>
<?import javafx.scene.shape.*?>
<?import java.lang.*?>
<?import javafx.scene.layout.*?>
<?import javafx.scene.Scene?>
<Pane maxHeight="-Infinity" maxWidth="-Infinity" minHeight="-Infinity" minWidth="-Infinity" prefHeight="40.0" prefWidth="40.0" xmlns="http://javafx.com/javafx/8" xmlns:fx="http://javafx.com/fxml/1">
<children>
<Circle fx:id="circle" fill="DODGERBLUE" layoutX="20.0" layoutY="20.0" radius="20.0" stroke="BLACK" strokeType="INSIDE" />
</children>
</Pane>
package application;
import java.io.IOException;
import javafx.fxml.FXML;
import javafx.fxml.FXMLLoader;
import javafx.scene.layout.Parent;
import javafx.scene.shape.Circle;
public class myCircle extends Parent {
@FXML Circle circle;
public myCircle() {
// TODO Auto-generated constructor stub
FXMLLoader fxmlLoader = new FXMLLoader(getClass().getResource("myCircle.fxml"));
//fxmlLoader.setRoot(this);
fxmlLoader.setController(this);
try {
fxmlLoader.load();
} catch (IOException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
this.getChildren().add(circle);
System.out.println("generate myCircle");
}
}
In file: myCircle.java. I can't add code fxmlLoader.setRoot(this);
, or it will show the error message: "Root value already specified."
Is the root node dynamic loading? (I didn't use <fx:root>
or setRoot()
) If it is dynamic loading, which one is my current root node?
In file: myCircle.java. I must add line this.getChildren().add(circle);
, or there is no circle generate. Why? I think there is some important detail I don't know...
centerXProperty()
to implement binding line relative feature, but there is some problem. My custom UML shape apply and load custom fxml file, I couldn't get real centerXProperty. I print centerXProperty messege: DoubleProperty [bean: Circle[id=circle, centerX=0.0, centerY=0.0, radius=20.0, fill=0x1e90ffff, stroke=0x000000ff, strokeWidth=1.0], name: centerX, value: 0.0]
. The value always is 0.0 no matter what. How could I do?
I don't want to type spaghetti code.
The root element of your FXML file is a <Pane>
element, which is basically an instruction to the FXMLLoader
to create a Pane
instance, which becomes the root. Thus when you try to call fxmlLoader.setRoot(...)
it complains, because there is already a root specified in the FXML file. To fix this, you need to use <fx:root...>
as the root element:
<?xml version="1.0" encoding="UTF-8"?>
<?import javafx.scene.shape.*?>
<?import java.lang.*?>
<?import javafx.scene.layout.*?>
<?import javafx.scene.Scene?>
<fx:root type="Pane" maxHeight="-Infinity" maxWidth="-Infinity"
minHeight="-Infinity" minWidth="-Infinity"
prefHeight="40.0" prefWidth="40.0"
xmlns="http://javafx.com/javafx/8" xmlns:fx="http://javafx.com/fxml/1">
<children>
<Circle fx:id="circle" fill="DODGERBLUE" layoutX="20.0" layoutY="20.0" radius="20.0" stroke="BLACK" strokeType="INSIDE" />
</children>
</fx:root>
In order for fxmlLoader.setRoot(this)
to work, this
must be an instance of the class specified by the type
attribute to <fx:root>
, i.e. you must make myCircle
a subclass of Pane
:
package application;
import java.io.IOException;
import javafx.fxml.FXML;
import javafx.fxml.FXMLLoader;
import javafx.scene.layout.Pane;
import javafx.scene.shape.Circle;
public class myCircle extends Pane {
@FXML Circle circle;
public myCircle() {
// TODO Auto-generated constructor stub
FXMLLoader fxmlLoader = new FXMLLoader(getClass().getResource("myCircle.fxml"));
fxmlLoader.setRoot(this);
fxmlLoader.setController(this);
try {
fxmlLoader.load();
} catch (IOException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
// circle is now added to this by the FXML.
// So next line is no longer needed:
// this.getChildren().add(circle);
System.out.println("generate myCircle");
}
}
Is fixed by fixing fxmlLoader.setRoot(this)
. The circle is a child of the Pane
that is the root of the FMXL file. Previously, it wasn't added to the myCircle
instance unless you did so explicitly.
You never change the centerX
and centerY
properties, so they will always be 0. Did you mean to use centerX="20"
instead of layoutX="20"
in the FXML? If you want to expose them for binding/setting/etc you can do so in your myCircle
class:
package application;
import java.io.IOException;
import javafx.beans.property.DoubleProperty ;
import javafx.fxml.FXML;
import javafx.fxml.FXMLLoader;
import javafx.scene.layout.Pane;
import javafx.scene.shape.Circle;
public class myCircle extends Pane {
@FXML Circle circle;
public myCircle() {
// TODO Auto-generated constructor stub
FXMLLoader fxmlLoader = new FXMLLoader(getClass().getResource("myCircle.fxml"));
fxmlLoader.setRoot(this);
fxmlLoader.setController(this);
try {
fxmlLoader.load();
} catch (IOException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
// circle is now added to this by the FXML.
// So next line is no longer needed:
// this.getChildren().add(circle);
System.out.println("generate myCircle");
}
public DoubleProperty centerXProperty() {
return circle.centerXProperty();
}
public final double getCenterX() {
return centerXProperty().get();
}
public final void setCenterX(double centerX) {
centerXProperty().set(centerX);
}
// same for centerY...
}
You could also consider just making MyCircle
a subclass of Circle
, so that you simply inherit the centerX
and centerY
properties. i.e.
package application;
import java.io.IOException;
import javafx.fxml.FXML;
import javafx.fxml.FXMLLoader;
import javafx.scene.shape.Circle;
public class myCircle extends Circle {
public myCircle() {
// TODO Auto-generated constructor stub
FXMLLoader fxmlLoader = new FXMLLoader(getClass().getResource("myCircle.fxml"));
fxmlLoader.setRoot(this);
fxmlLoader.setController(this);
try {
fxmlLoader.load();
} catch (IOException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
System.out.println("generate myCircle");
}
}
and then
<?xml version="1.0" encoding="UTF-8"?>
<?import javafx.scene.shape.*?>
<?import java.lang.*?>
<?import javafx.scene.layout.*?>
<?import javafx.scene.Scene?>
<fx:root type="Circle" fill="DODGERBLUE" layoutX="20.0" layoutY="20.0"
radius="20.0" stroke="BLACK" strokeType="INSIDE"
xmlns="http://javafx.com/javafx/8" xmlns:fx="http://javafx.com/fxml/1">
</fx:root>
though I'm not sure if this still gives you all the functionality you want.
User contributions licensed under CC BY-SA 3.0