JavaFX - 如何修复 'fx:controller can only be applied to root element'
JavaFX - How to fix 'fx:controller can only be applied to root element'
我用 JavaFX 创建了一个简单的无边界应用程序。现在我创建了一个标签,它应该替换丢失的 "close" 按钮。实际上,我的尝试没有奏效,因为我无法与我的 FXMLController 交互。
我已经尝试将 FXMLController 添加到我的 *.fxml 文件中。我尝试将控制器插入 VBox 和 AnchorPane,但两者都不起作用,我也不太明白。
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();
}
}
堆栈跟踪
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(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(LauncherImpl.java:846)
at com.sun.javafx.application.PlatformImpl.lambda$runAndWait(PlatformImpl.java:455)
at com.sun.javafx.application.PlatformImpl.lambda$runLater(PlatformImpl.java:428)
at java.base/java.security.AccessController.doPrivileged(Native Method)
at com.sun.javafx.application.PlatformImpl.lambda$runLater(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(WinApplication.java:174)
... 1 more
我希望应用程序启动,当我按下 "X" 标签时应用程序将关闭。
快速提醒:我是 JavaFX 的新手,所以我也非常感谢每一个 JavaFX 教程推荐。
I tried to insert the controller to the VBox and the AnchorPane but both are not working and I don't really get it.
您提到尝试将 fx:controller
属性同时放在 VBox
元素和 AnchorPane
元素上。您显示的代码是您尝试将其添加到 AnchorPane
元素。 运行 你的代码在我的电脑上的结果是你观察到的 LoadException
。通过将 fx:controller
属性放在 VBox
元素(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 -->
注意:我最初注意到另一个问题,您没有关闭 <VBox>
或 <children>
元素。但是,那些结束标签一直都在那里;他们只是没有在您的问题中呈现,。我忘了检查这个。
但是,这会导致不同的异常。
// 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(LauncherImpl.java:846)
at javafx.graphics/com.sun.javafx.application.PlatformImpl.lambda$runAndWait(PlatformImpl.java:455)
at javafx.graphics/com.sun.javafx.application.PlatformImpl.lambda$runLater(PlatformImpl.java:428)
at java.base/java.security.AccessController.doPrivileged(Native Method)
at javafx.graphics/com.sun.javafx.application.PlatformImpl.lambda$runLater(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(WinApplication.java:174)
... 1 more
这个问题最终是由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
由于您正在使用 fx:controller
,因此 FXMLLoader
将创建 自己的 FXMLDocumentController
实例。正是在这个实例中, @FXML
注释字段被注入。另一方面,您创建您自己的实例,该实例不会注入字段。您应该使用由 FXMLLoader
.
创建的 FXMLDocumentController
实例
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();
但是,虽然您的代码(包含到目前为止的修复)将执行您想要的操作,但这并不是正确的方法。方法 closeLabelPressed
看起来像您打算将它作为事件处理程序,因为您用 @FXML
对其进行了注释。但是,您不会 link 使用 FXML 文件使用此方法,而是该方法将 EventHandler
添加到 Label
。设置正确,你根本不应该从 LabelTest
调用 closeLabelPressed
。
您应该做的第一件事是 link 通过 FXML 文件 Label
的方法。
<!-- 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 -->
注意 onMousePressed
属性。
第二件事是改变FXMLDocumentController
中的closeLabelPressed
方法。
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();
}
}
现在,当按下 Label
时调用该方法。在内部,FXMLLoader
将 onMousePressed
属性(属于 labelTest
)设置为调用 closeLabelPressed
的 EventHandler
。我还将 System.exit(0)
的用法更改为 Platform.exit()
。后者将允许 JavaFX 运行时正常关闭(允许您在 Application#stop
等地方进行清理),而前者会立即终止 JVM(通常不会 return)。 注意:可能不再需要注入 Label
。
要做的第三件事是删除LabelTest.start
方法中对closeLabelPressed
的调用。
进行这些更改后,您的代码应该可以正常工作。
至于要求教程(在 Stack Overflow 上是题外话,请参阅 Stack Overflow 上与 FXML 相关的 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。
我用 JavaFX 创建了一个简单的无边界应用程序。现在我创建了一个标签,它应该替换丢失的 "close" 按钮。实际上,我的尝试没有奏效,因为我无法与我的 FXMLController 交互。
我已经尝试将 FXMLController 添加到我的 *.fxml 文件中。我尝试将控制器插入 VBox 和 AnchorPane,但两者都不起作用,我也不太明白。
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();
}
}
堆栈跟踪
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(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(LauncherImpl.java:846)
at com.sun.javafx.application.PlatformImpl.lambda$runAndWait(PlatformImpl.java:455)
at com.sun.javafx.application.PlatformImpl.lambda$runLater(PlatformImpl.java:428)
at java.base/java.security.AccessController.doPrivileged(Native Method)
at com.sun.javafx.application.PlatformImpl.lambda$runLater(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(WinApplication.java:174)
... 1 more
我希望应用程序启动,当我按下 "X" 标签时应用程序将关闭。
快速提醒:我是 JavaFX 的新手,所以我也非常感谢每一个 JavaFX 教程推荐。
I tried to insert the controller to the VBox and the AnchorPane but both are not working and I don't really get it.
您提到尝试将 fx:controller
属性同时放在 VBox
元素和 AnchorPane
元素上。您显示的代码是您尝试将其添加到 AnchorPane
元素。 运行 你的代码在我的电脑上的结果是你观察到的 LoadException
。通过将 fx:controller
属性放在 VBox
元素(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 -->
注意:我最初注意到另一个问题,您没有关闭 <VBox>
或 <children>
元素。但是,那些结束标签一直都在那里;他们只是没有在您的问题中呈现,
但是,这会导致不同的异常。
// 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(LauncherImpl.java:846)
at javafx.graphics/com.sun.javafx.application.PlatformImpl.lambda$runAndWait(PlatformImpl.java:455)
at javafx.graphics/com.sun.javafx.application.PlatformImpl.lambda$runLater(PlatformImpl.java:428)
at java.base/java.security.AccessController.doPrivileged(Native Method)
at javafx.graphics/com.sun.javafx.application.PlatformImpl.lambda$runLater(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(WinApplication.java:174)
... 1 more
这个问题最终是由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
由于您正在使用 fx:controller
,因此 FXMLLoader
将创建 自己的 FXMLDocumentController
实例。正是在这个实例中, @FXML
注释字段被注入。另一方面,您创建您自己的实例,该实例不会注入字段。您应该使用由 FXMLLoader
.
FXMLDocumentController
实例
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();
但是,虽然您的代码(包含到目前为止的修复)将执行您想要的操作,但这并不是正确的方法。方法 closeLabelPressed
看起来像您打算将它作为事件处理程序,因为您用 @FXML
对其进行了注释。但是,您不会 link 使用 FXML 文件使用此方法,而是该方法将 EventHandler
添加到 Label
。设置正确,你根本不应该从 LabelTest
调用 closeLabelPressed
。
您应该做的第一件事是 link 通过 FXML 文件 Label
的方法。
<!-- 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 -->
注意 onMousePressed
属性。
第二件事是改变FXMLDocumentController
中的closeLabelPressed
方法。
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();
}
}
现在,当按下 Label
时调用该方法。在内部,FXMLLoader
将 onMousePressed
属性(属于 labelTest
)设置为调用 closeLabelPressed
的 EventHandler
。我还将 System.exit(0)
的用法更改为 Platform.exit()
。后者将允许 JavaFX 运行时正常关闭(允许您在 Application#stop
等地方进行清理),而前者会立即终止 JVM(通常不会 return)。 注意:可能不再需要注入 Label
。
要做的第三件事是删除LabelTest.start
方法中对closeLabelPressed
的调用。
进行这些更改后,您的代码应该可以正常工作。
至于要求教程(在 Stack Overflow 上是题外话,请参阅 Stack Overflow 上与 FXML 相关的 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。