Reputation: 380
I have a fxml file build from fxml builder and I am using it by a loader in Java.
URL resource = getClass().getClassLoader().getResource("fxmlFile.fxml");
FXMLLoader loader = new FXMLLoader(resource, resourceBundle);
Pane rootPane = (Pane) loader.load();
this fxml file maps click event to my class;
<Group id="Group" layoutX="0.0" layoutY="0.0" onMouseReleased="#handleThis" scaleX="1.0" scaleY="1.0">
...
<Group/>
so I implement my handler in my class, lets call it MyClass;
public class MyClass {
public void createScene() throws IOException
{
URL resource = getClass().getClassLoader().getResource("fxmlFile.fxml");
FXMLLoader loader = new FXMLLoader(resource, resourceBundle);
Pane rootPane = (Pane) loader.load();
...
}
@FXML
public void handleThis(ActionEvent event) {
System.out.println("from MyClass");
}
...
}
Now I extend MyClass as MyExtendedClass and override handleThis method;
public class MyExtendedClass extends MyClass {
@Override
public void handleThis(ActionEvent event) {
System.out.println("from MyExtendedClass");
}
}
My question is, I cannot manage to work handle method in my extended class. It does not overrides it. How can I achieve to make it print "from MyExtendedClass" instead of "from MyClass"?
Upvotes: 1
Views: 2446
Reputation: 1015
When createScene()
is called on an instance of MyExtendedClass, the FXMLLoader parses the FXML file, reads the fx:controller="MyClass"
attribute and instantiates a new object of type MyClass. That is why the base method is always called. The FXMLLoader doesn't know about MyExtendedClass.
There is a - hackish - way to achieve what you want (i.e. doing the loading in MyClass and still defining the controller in FXML):
public class MyClass
{
public void createScene()
{
try
{
FXMLLoader loader = new FXMLLoader();
loader.setLocation(getClass().getResource("FXML.fxml"));
// set a controller factory that returns this instance as controller
// (works in this case, but not recommended)
loader.setControllerFactory(controllerType -> this);
pane = (Pane) loader.load();
}
catch (IOException e)
{
throw new RuntimeException(e);
}
}
}
It would be cleaner to instantiate the controller and pass it to the FXMLLoader.
For this the fx:controller=""
attribute must be removed from the FXML file.
public class Main extends Application
{
@Override
public void start(Stage primaryStage) throws Exception
{
MyClass controller = new MyExtendedClass();
FXMLLoader loader = new FXMLLoader();
loader.setLocation(getClass().getResource("FXML.fxml"));
loader.setController(controller);
Pane pane = (Pane) loader.load();
primaryStage.setScene(new Scene(pane));
primaryStage.show();
}
public static void main(String[] args)
{
launch(args);
}
}
Or use fx:controller="MyClass"
to define the base type in the FXML file and let a controller factory decide the actual implementation.
public class Main extends Application
{
@Override
public void start(Stage primaryStage) throws Exception
{
FXMLLoader loader = new FXMLLoader();
loader.setLocation(getClass().getResource("FXML.fxml"));
loader.setControllerFactory(controllerType -> {
if (MyClass.class.equals(controllerType))
return new MyExtendedClass();
else
return null; // return some other controller
});
Pane pane = (Pane) loader.load();
MyClass controller = (MyClass) loader.getController();
primaryStage.setScene(new Scene(pane));
primaryStage.show();
}
public static void main(String[] args)
{
launch(args);
}
}
Upvotes: 1