I have created a simple borderless application with JavaFX. Now I created a Label which should replace the missing "close" button. Actually, my attempt is not working because I am not able to interact with my FXMLController.
I have already tried to add the FXMLController to my *.fxml file. I tried to insert the controller to the VBox and the AnchorPane but both are not working and I don't really get it.
LabelCloseTest.fxml
<?xml version="1.0" encoding="UTF-8"?>
<?import javafx.scene.control.Label?>
<?import javafx.scene.layout.AnchorPane?>
<?import javafx.scene.layout.VBox?>
<?import javafx.scene.text.Font?>
<VBox prefHeight="720.0" prefWidth="1025.0"
xmlns="http://javafx.com/javafx/10.0.1" xmlns:fx="http://javafx.com/fxml/1">
<children>
<AnchorPane maxHeight="-1.0" maxWidth="-1.0" prefHeight="-1.0" prefWidth="-1.0" style="-fx-background-color: #303136;" VBox.vgrow="ALWAYS" fx:controller="com.mycompany.testing.FXMLDocumentController">
<children>
<Label fx:id="labelTest" layoutX="1007.0" layoutY="5.0" prefHeight="10.0" prefWidth="5.0" text="X" textFill="WHITE">
<font>
<Font name="System Bold" size="13.0" />
</font>
</Label>
</children>
</AnchorPane>
</children>
</VBox>
FXMLDocumentController.java
package com.mycompany.testing;
import javafx.event.EventHandler;
import javafx.fxml.FXML;
import javafx.scene.control.Label;
import javafx.scene.input.MouseEvent;
public class FXMLDocumentController {
@FXML
private Label labelTest;
@FXML
public void closeLabelPressed() {
labelTest.setOnMouseClicked(new EventHandler<MouseEvent>() {
public void handle(MouseEvent event) {
System.exit(0);
}
});
}
}
LabelTest.java
package com.mycompany.testing;
import javafx.application.Application;
import javafx.fxml.FXMLLoader;
import javafx.scene.Parent;
import javafx.scene.Scene;
import javafx.stage.Stage;
import javafx.stage.StageStyle;
public class LabelTest extends Application {
public static void main (String[] args) {
launch(args);
}
@Override
public void start(Stage primaryStage) throws Exception {
primaryStage.initStyle(StageStyle.UNDECORATED);
FXMLLoader loader = new FXMLLoader(getClass().getResource("/fxml/LabelCloseTest.fxml"));
Parent root = loader.load();
FXMLDocumentController fdc = new FXMLDocumentController();
fdc.closeLabelPressed();
Scene scene = new Scene(root);
primaryStage.setScene(scene);
primaryStage.show();
}
}
Stacktrace
Exception in Application start method
Exception in thread "main" java.lang.RuntimeException: Exception in Application start method
at com.sun.javafx.application.LauncherImpl.launchApplication1(LauncherImpl.java:900)
at com.sun.javafx.application.LauncherImpl.lambda$launchApplication$2(LauncherImpl.java:195)
at java.base/java.lang.Thread.run(Thread.java:834)
Caused by: javafx.fxml.LoadException: fx:controller can only be applied to root element.
/D:/Workspace/SocketClient/target/classes/fxml/LabelCloseTest.fxml:10
at javafx.fxml.FXMLLoader.constructLoadException(FXMLLoader.java:2621)
at javafx.fxml.FXMLLoader$ValueElement.processAttribute(FXMLLoader.java:917)
at javafx.fxml.FXMLLoader$InstanceDeclarationElement.processAttribute(FXMLLoader.java:980)
at javafx.fxml.FXMLLoader$Element.processStartElement(FXMLLoader.java:227)
at javafx.fxml.FXMLLoader$ValueElement.processStartElement(FXMLLoader.java:752)
at javafx.fxml.FXMLLoader.processStartElement(FXMLLoader.java:2722)
at javafx.fxml.FXMLLoader.loadImpl(FXMLLoader.java:2552)
at javafx.fxml.FXMLLoader.loadImpl(FXMLLoader.java:2466)
at javafx.fxml.FXMLLoader.load(FXMLLoader.java:2435)
at com.mycompany.testing.LabelTest.start(LabelTest.java:25)
at com.sun.javafx.application.LauncherImpl.lambda$launchApplication1$9(LauncherImpl.java:846)
at com.sun.javafx.application.PlatformImpl.lambda$runAndWait$12(PlatformImpl.java:455)
at com.sun.javafx.application.PlatformImpl.lambda$runLater$10(PlatformImpl.java:428)
at java.base/java.security.AccessController.doPrivileged(Native Method)
at com.sun.javafx.application.PlatformImpl.lambda$runLater$11(PlatformImpl.java:427)
at com.sun.glass.ui.InvokeLaterDispatcher$Future.run(InvokeLaterDispatcher.java:96)
at com.sun.glass.ui.win.WinApplication._runLoop(Native Method)
at com.sun.glass.ui.win.WinApplication.lambda$runLoop$3(WinApplication.java:174)
... 1 more
I would expect that the application starts and when I press the "X" Label that the Application would close.
Quick reminder: I am new to JavaFX so I would also be thankful for every JavaFX tutorial recommendation.
I tried to insert the controller to the VBox and the AnchorPane but both are not working and I don't really get it.
You mention trying to put the fx:controller
attribute both on the VBox
element as well as the AnchorPane
element. The code you show is your attempt to add it to the AnchorPane
element. Running your code on my computer as is results in the LoadException
you observe. This problem is solved by putting the fx:controller
attribute in the VBox
element (the root element ):
<VBox prefHeight="720.0" prefWidth="1025.0"
xmlns="http://javafx.com/javafx/10.0.1" xmlns:fx="http://javafx.com/fxml/1"
fx:controller="com.mycompany.testing.FXMLDocumentController">
<!--- Rest of FXML file omitted for brevity -->
Note : I initially noticed another problem where you weren't closing the <VBox>
or <children>
elements. However, those closing tags were there the whole time; they just weren't rendering in your question, as pointed out by fabian . I neglected to check for this.
However , this results in a different exception.
// Beginning of stack trace omitted for brevity...
Caused by: java.lang.NullPointerException
at com.mycompany.testing.FXMLDocumentController.closeLabelPressed(FXMLDocumentController.java:16)
at com.mycompany.testing.LabelTest.start(LabelTest.java:28)
at javafx.graphics/com.sun.javafx.application.LauncherImpl.lambda$launchApplication1$9(LauncherImpl.java:846)
at javafx.graphics/com.sun.javafx.application.PlatformImpl.lambda$runAndWait$12(PlatformImpl.java:455)
at javafx.graphics/com.sun.javafx.application.PlatformImpl.lambda$runLater$10(PlatformImpl.java:428)
at java.base/java.security.AccessController.doPrivileged(Native Method)
at javafx.graphics/com.sun.javafx.application.PlatformImpl.lambda$runLater$11(PlatformImpl.java:427)
at javafx.graphics/com.sun.glass.ui.InvokeLaterDispatcher$Future.run(InvokeLaterDispatcher.java:96)
at javafx.graphics/com.sun.glass.ui.win.WinApplication._runLoop(Native Method)
at javafx.graphics/com.sun.glass.ui.win.WinApplication.lambda$runLoop$3(WinApplication.java:174)
... 1 more
This problem is ultimately caused by this code in LabelTest#start
:
FXMLLoader loader = new FXMLLoader(getClass().getResource("/fxml/LabelCloseTest.fxml"));
Parent root = loader.load();
FXMLDocumentController fdc = new FXMLDocumentController(); // the real problem
fdc.closeLabelPressed(); // accesses the labelTest field which is null
Since you're using fx:controller
the FXMLLoader
will create its own instance of FXMLDocumentController
. It is this instance where the @FXML
annotated fields get injected. You, on the other hand, create your own instance which will not have had the fields injected. You should be using the FXMLDocumentController
instance created by the FXMLLoader
.
FXMLLoader loader = new FXMLLoader(getClass().getResource("/fxml/LabelCloseTest.fxml"));
Parent root = loader.load();
// gets instance from FXMLLoader, must be called after load()
FXMLDocumentController fdc = loader.getController();
fdc.closeLabelPressed();
However , while your code (with the fixes so far) will do what you want it is not the correct way to do it. The method closeLabelPressed
looks like you intend it to be an event handler since you annotated it with @FXML
. You don't link this method with the FXML file, however, and instead the method adds an EventHandler
to the Label
. Set up correctly, you shouldn't be calling closeLabelPressed
from LabelTest
at all.
The first thing you should do is link the method to the Label
via the FXML file.
<!-- previous omitted for brevity -->
<Label fx:id="labelTest" layoutX="1007.0" layoutY="5.0" prefHeight="10.0" prefWidth="5.0" text="X"
textFill="WHITE" onMousePressed="#closeLabelPressed">
<!-- rest omitted for brevity -->
Notice the onMousePressed
attribute .
The second thing to do is to change closeLabelPressed
method in FXMLDocumentController
.
package com.mycompany.testing;
import javafx.application.Platform;
import javafx.fxml.FXML;
import javafx.scene.control.Label;
import javafx.scene.input.MouseEvent;
public class FXMLDocumentController {
@FXML
private Label labelTest;
@FXML
private void closeLabelPressed(MouseEvent event) {
Platform.exit();
}
}
Now the method is called when the Label
is pressed. Internally, the FXMLLoader
sets the onMousePressed
property (of labelTest
) to an EventHandler
that invokes closeLabelPressed
. I've also changed the use of System.exit(0)
to Platform.exit()
. The latter will allow the JavaFX runtime to gracefully shutdown (allowing you to do clean up in places like Application#stop
) while the former immediately terminates the JVM (and doesn't return normally). Note: Injecting the Label
may no longer be necessary .
The third thing to do is to remove the call to closeLabelPressed
in the LabelTest.start
method.
After these changes your code should be working properly.
As for asking for tutorials (which is off-topic on Stack Overflow, see the help center ) you can likely find many by searching Google. For "official" sources, see JavaFX: Getting started with JavaFX (last updated for JavaFX 8) and Introduction to FXML . There are also many questions related to FXML here on Stack Overflow.
The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.