Spring injection with new Window (FXML) - javafx

I use AnnotationConfigApplicationContext to load my spring configuration.
public class Main extends Application {
private AnnotationConfigApplicationContext applicationContext;
#Override
public void init() throws Exception {
applicationContext = new AnnotationConfigApplicationContext(ApplicationConfig.class);
}
#Override
public void start(Stage primaryStage) throws Exception{
FXMLLoader loader = new FXMLLoader(getClass().getResource("/org/test/view/main.fxml"));
loader.setControllerFactory(applicationContext::getBean);
Parent root = loader.load();
primaryStage.show();
primaryStage.setOnHidden(e -> Platform.exit());
}
and this is my ApplicationConfig
#Configuration
#ComponentScan
public class ApplicationConfig {
#Bean
public Executor executor() {
return Executors.newCachedThreadPool(r -> {
Thread t = new Thread(r);
t.setDaemon(true);
return t ;
});
}
Now I would like to inject this Executor instance into a new Stage (TestController.fxml) via FXMLLoader. How can I achieve that?
public void showNewWindow() {
try {
FXMLLoader fxmlLoader = new FXMLLoader(getClass().getResource("/org/test/view/TestController.fxml"));
Parent root1 = fxmlLoader.load();
Stage stage = new Stage();
stage.setScene(new Scene(root1));
stage.show();
} catch (Exception e) {
e.printStackTrace();
}
}
All help will be greatly appreciated. Thanks!

You can inject "well-known objects" into spring-managed beans. The application context itself is one such object, so in your main controller you can do:
public class MainController {
#Autowired
private ApplicationContext applicationContext ;
public void showNewWindow() {
try {
FXMLLoader fxmlLoader = new FXMLLoader(getClass().getResource("/org/test/view/TestController.fxml"));
fxmlLoader.setControllerFactory(applicationContext::getBean);
Parent root1 = fxmlLoader.load();
Stage stage = new Stage();
stage.setScene(new Scene(root1));
stage.show();
} catch (Exception e) {
e.printStackTrace();
}
}
// ...
}
Now the TestController will be spring-managed, so you can simply do
public class TestController {
#Autowired
private Executor executor ;
// ...
}

Related

JavaFX - How to switch to another tab with mouse click event [duplicate]

So I'm trying to load and save Images into an imageView where the location of the image is chosen through a file browser. I've been working on this for several days now and I'm gonna have a stroke if I can't get it fixed. I've tried everything I can think of. Thank you in advance for helping.
UPDATED:
Here is my main class:
public class Main extends Application {
private Stage primaryStage;
private BorderPane rootLayout;
public Main(){}
#Override
public void start(Stage primaryStage) throws Exception{
this.primaryStage = primaryStage;
this.primaryStage.setTitle("Help Please");
initRootLayout();
showScreen();
}
public void initRootLayout(){
try{
FXMLLoader loader = new FXMLLoader();
loader.setLocation(Main.class.getResource("view/RootLayout.fxml"));
rootLayout = (BorderPane) loader.load();
Scene scene = new Scene(rootLayout);
primaryStage.setScene(scene);
RootLayout controller = loader.getController();
controller.setMain(this);
primaryStage.show();
}catch(Exception e ){e.printStackTrace();}
}
public void showScreen(){
try{FXMLLoader loader = new FXMLLoader();
loader.setLocation(Main.class.getResource("view/sample.fxml"));
BorderPane sample = (BorderPane)loader.load();
rootLayout.setCenter(sample);
Controller controller = loader.getController();
controller.setMain(this);
}catch (Exception e){e.printStackTrace();}
}
public Stage getPrimaryStage(){return primaryStage;}
public static void main(String[] args) {
launch(args);
}
}
Here is the rootLayout:
public class RootLayout {
private Main main;
private Controller controller = new Controller();
public void setMain(Main main){this.main = main;}
#FXML
private void handleOpen(){
FileChooser fileChooser = new FileChooser();
FileChooser.ExtensionFilter extensionFilter = new FileChooser.ExtensionFilter(
"PNG files (*.png)","*png");
fileChooser.getExtensionFilters().add(extensionFilter);
File file = fileChooser.showOpenDialog(main.getPrimaryStage());
if(file!= null){
controller.updateImage(file.toURI().toString());
}
}
}
And here is the controller:
public class Controller implements Initializable {
#FXML
ImageView imageView = new ImageView();
String imageURL;
Main main = new Main();
public void setMain(Main main){
this.main = main;
}
public void updateImage(String url){
if(url.length()>=1){
Image image = new Image(url);
imageView.setImage(image);
System.out.println(url);
}
else{
System.out.println(url);
System.out.println("image invalid");
}
}
#Override
public void initialize(URL location, ResourceBundle resources) {
}
}
Two things:
Never assign a field whose value is to be injected by an FXMLLoader (e.g. #FXML fields). Doing so is a waste of resources at best and introduces subtle bugs at worst. For instance, if you were to leave the imageView field uninitialized you'd be getting a NullPointerException which would indicate a problem with your setup. Since you do initialize the field, however, you don't get any errors and there's a false impression of the code working.
In your RootLayout controller class, you have:
private Controller controller = new Controller();
That instance of Controller you just created is not linked to any FXML file. And since you initialize the imageView field (see first point) you end up updating an ImageView which is not being displayed anywhere; this is where not initializing said field would have given a nice indication of there being a problem. The solution is to pass the Controller instance created by the FXMLLoader to the RootLayout instance created by the other FXMLLoader.
Also, in the same class you have:
Main main = new Main();
Which is also unnecessary since the created instance of Main is both not the correct instance and is replaced by the call to #setMain(Main) almost immediately.
Assuming your FXML files (which you did not provide) are correct, the Java classes should look more like:
Main.java
public class Main extends Application {
private Stage primaryStage;
private BorderPane rootLayout;
private RootLayout rootLayoutController;
public Main() {}
#Override
public void start(Stage primaryStage) throws Exception {
this.primaryStage = primaryStage;
this.primaryStage.setTitle("Help Please");
initRootLayout();
showScreen();
}
public void initRootLayout() {
try {
FXMLLoader loader = new FXMLLoader();
loader.setLocation(Main.class.getResource("view/RootLayout.fxml"));
rootLayout = (BorderPane) loader.load();
Scene scene = new Scene(rootLayout);
primaryStage.setScene(scene);
// store RootLayout instance in field so #showScreen()
// can reference it
rootLayoutController = loader.getController();
rootLayoutController.setMain(this);
primaryStage.show();
} catch (Exception e) {
e.printStackTrace();
}
}
public void showScreen() {
try {
FXMLLoader loader = new FXMLLoader();
loader.setLocation(Main.class.getResource("view/sample.fxml"));
BorderPane sample = (BorderPane) loader.load();
rootLayout.setCenter(sample);
Controller controller = loader.getController();
controller.setMain(this);
// set Controller instance on RootLayout instance
rootLayoutController.setController(controller);
} catch (Exception e) {
e.printStackTrace();
}
}
public Stage getPrimaryStage() {
return primaryStage;
}
public static void main(String[] args) {
launch(args);
}
}
RootLayout.java
public class RootLayout {
private Main main;
private Controller controller;
public void setMain(Main main) {
this.main = main;
}
public void setController(Controller controller) {
this.controller = controller;
}
#FXML
private void handleOpen() {
FileChooser fileChooser = new FileChooser();
// Note extensions should be prefixed with "*."
FileChooser.ExtensionFilter extensionFilter =
new FileChooser.ExtensionFilter("PNG files (*.png)", "*.png");
fileChooser.getExtensionFilters().add(extensionFilter);
File file = fileChooser.showOpenDialog(main.getPrimaryStage());
if (file != null) {
controller.updateImage(file.toURI().toString());
}
}
}
Controller.java
public class Controller implements Initializable {
#FXML ImageView imageView; // leave uninitialized, will be injected
String imageURL;
Main main;
public void setMain(Main main) {
this.main = main;
}
public void updateImage(String url) {
if (url.length() >= 1) {
Image image = new Image(url);
imageView.setImage(image);
System.out.println(url);
} else {
System.out.println(url);
System.out.println("image invalid");
}
}
#Override
public void initialize(URL location, ResourceBundle resources) {}
}
Note: Did not test new code.

How can I open a new Scene from a different class

I have a simple button that i want to click and open a new scene but i made the new scene in another class but it doesn't work and it does not show my errors either.
Here is my Controller class
public class Controller
{
public TextField txtUsername;
public TextField txtEmail;
public TextField txtPass;
public TextField txtPhone;
public Button btnLogin;
}
public void buttonClicked()
{
Chattingform chattingform = new Chattingform ();
}
Here is my Chattingform class
public class Chattingform extends Application
{
#Override
public void start (Stage primaryStage) throws Exception {
FXMLLoader fxmlLoader = new FXMLLoader(getClass().getResource("Chattingform.fxml"));
try {
Parent root = fxmlLoader.load();
Stage stage = new Stage();
stage.initModality(Modality.APPLICATION_MODAL);
stage.setOpacity(1);
stage.setTitle("Exchat");
stage.setScene(new Scene (root, 450, 450));
stage.showAndWait();
}catch ( IOException io )
{
JOptionPane.showMessageDialog (null, "Could not open ");
}
}
From my experince, all JavaFX Controllers have:
#Override
public void initialize(URL url, ResourceBundle rb) {
// TODO
}

how to add nodes on a root pane from a different class?

I'm trying to add a node on my root pane. If i try to call a method outside the class, nodes are not displayed on the root pane. However if i call the method within the class, it works.
public class MainLayout extends AnchorPane{
#FXML public Pane root_pane;
public MainLayout(){
FXMLLoader fxmlLoader = new FXMLLoader(getClass().getResource("/resources/MainLayout.fxml"));
fxmlLoader.setRoot(this);
fxmlLoader.setController(this);
try{
fxmlLoader.load();
}catch(IOException exception){
throw new RuntimeException(exception);
}
}
#FXML
private void initialize(){
ShapeEllipse ellipse = new ShapeEllipse();
ellipse.setText("Main");
Arrows arrow = new Arrows();
arrow.relocate(10,20);
root_pane.getChildren().addAll(ellipse,arrow);
}
public void insertShape(){
ShapeInputOutput inputOutput = new ShapeInputOutput();
inputOutput.relocate(10,50);
root_pane.getChildren().add(inputOutput);
}
Here's my 2nd controller class
public class AddShapes extends AnchorPane{
MainLayout main = new MainLayout();
ShapeInputOutput inputOutput1 = new ShapeInputOutput();
public AddShapes(){
FXMLLoader fxmlLoader = new FXMLLoader();
fxmlLoader.setLocation(getClass().getResource("/resources/AddShapes.fxml"));
fxmlLoader.setRoot(this);
fxmlLoader.setController(this);
try{
fxmlLoader.load();
}catch(Exception e){
e.printStackTrace();
}
addActions();
}
public void addActions(){
inputOutput1.setOnMouseClicked(new EventHandler<MouseEvent>(){
#Override
public void handle(MouseEvent event) {
main.insertShape();
}

Instance of FXML controller in JavaFX returns null

I want to create a reference for the FXML Controller by this line in Class Main
Controller controller = (Controller) loader.getController();
to have access to a Controller methode from the Class GrblListener.
Main.controller.updateCurrentPosition(input);
But I always get the error
Exception in thread "EventThread COM5" java.lang.NullPointerException
What's wrong?
Class Main:
public class Main extends Application {
public static Stage stage;
public static Controller controller;
#Override
public void start(Stage stage) throws Exception {
FXMLLoader loader = new FXMLLoader(getClass().getResource("Scene.fxml"));
Parent root = (Parent) loader.load();
Controller controller = (Controller) loader.getController();
Scene scene = new Scene(root);
scene.getStylesheets().add(getClass().getResource("css/default.css").toExternalForm());
stage.setTitle("...");
stage.setScene(scene);
stage.show();
this.stage = stage;
}
public static void main(String[] args) {
launch(args);
}
}
Class GrblListener:
class GrblListener implements SerialPortEventListener {
#Override
public void serialEvent(SerialPortEvent event) {
if(event.isRXCHAR() && event.getEventValue() > 0){
try {
String input = GrblSender.serialPort.readString();
System.out.println(input.trim());
Main.controller.updateCurrentPosition(input);
}
catch (SerialPortException ex) {
System.out.println(ex);
}
}
}
}
You are declaring a local variable in start(), and initializing it:
Controller controller = (Controller) loader.getController();
instead of initializing the static variable you declared:
public static Controller controller ;
public void start(Stage stage) {
controller = (Controller) loader.getController();
// ...
}

JavaFx handler override

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"?
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);
}
}

Resources