简体   繁体   English

JavaFX - 如何修复'fx:controller只能应用于根元素'

[英]JavaFX - How to fix 'fx:controller can only be applied to root element'

I have created a simple borderless application with JavaFX. 我用JavaFX创建了一个简单的无边框应用程序。 Now I created a Label which should replace the missing "close" button. 现在我创建了一个Label,它应该替换丢失的“关闭”按钮。 Actually, my attempt is not working because I am not able to interact with my FXMLController. 实际上,我的尝试不起作用,因为我无法与我的FXMLController进行交互。

I have already tried to add the FXMLController to my *.fxml file. 我已经尝试将FXMLController添加到我的* .fxml文件中。 I tried to insert the controller to the VBox and the AnchorPane but both are not working and I don't really get it. 我试图将控制器插入VBox和AnchorPane,但两者都没有工作,我真的没有得到它。

LabelCloseTest.fxml 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 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 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. 我希望应用程序启动,当我按下应用程序关闭的“X”标签时。

Quick reminder: I am new to JavaFX so I would also be thankful for every JavaFX tutorial recommendation. 快速提醒:我是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. 我试图将控制器插入VBox和AnchorPane,但两者都没有工作,我真的没有得到它。

You mention trying to put the fx:controller attribute both on the VBox element as well as the AnchorPane element. 你提到尝试将fx:controller属性放在VBox元素和AnchorPane元素上。 The code you show is your attempt to add it to the AnchorPane element. 您显示的代码是您尝试将其添加到AnchorPane元素。 Running your code on my computer as is results in the LoadException you observe. 在我的计算机上运行代码会导致您观察到的LoadException This problem is solved by putting the fx:controller attribute in the VBox element (the root element ): 通过将fx:controller属性放在VBox元素( 根元素 )中来解决此问题:

<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. 注意 :我最初注意到你没有关闭<VBox><children>元素的另一个问题。 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 : 这个问题最终是由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 . 由于您正在使用fx:controllerFXMLLoader将创建自己FXMLDocumentController实例。 It is this instance where the @FXML annotated fields get injected. 正是这个实例注入了@FXML注释字段。 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 . 你应该使用FXMLDocumentController由创建的实例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 . closeLabelPressed方法看起来好像你打算成为一个事件处理程序,因为你用@FXML注释它。 You don't link this method with the FXML file, however, and instead the method adds an EventHandler to the Label . 但是,您不会将此方法与FXML文件链接,而是将该EventHandler添加到Label Set up correctly, you shouldn't be calling closeLabelPressed from LabelTest at all. 设置正确,你根本不应该从LabelTest调用closeLabelPressed

The first thing you should do is link the method to the Label via the FXML file. 您应该做的第一件事是通过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 -->

Notice the onMousePressed attribute . 注意onMousePressed属性

The second thing to do is to change closeLabelPressed method in FXMLDocumentController . 第二件事是在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();
    }
}

Now the method is called when the Label is pressed. 现在,在按下Label时调用该方法。 Internally, the FXMLLoader sets the onMousePressed property (of labelTest ) to an EventHandler that invokes closeLabelPressed . 在内部, FXMLLoaderonMousePressed属性( labelTest )设置为调用closeLabelPressedEventHandler I've also changed the use of System.exit(0) to Platform.exit() . 我还将System.exit(0)的使用更改为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). 后者将允许JavaFX运行时正常关闭(允许您在Application#stop等位置进行清理),而前者会立即终止JVM(并且不会正常返回)。 Note: Injecting the Label may no longer be necessary . 注意:可能不再需要注入Label

The third thing to do is to remove the call to closeLabelPressed in the LabelTest.start method. 第三个要做的是去除调用closeLabelPressedLabelTest.start方法。

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. 至于要求教程(这是关于Stack Overflow的偏离主题,请参阅帮助中心 ),您可以通过搜索Google找到许多教程。 For "official" sources, see JavaFX: Getting started with JavaFX (last updated for JavaFX 8) and Introduction to FXML . 有关“官方”资源,请参阅JavaFX:JavaFX入门 (最新更新为JavaFX 8)和FXML简介 There are also many questions related to FXML here on Stack Overflow. Stack Overflow上还有许多与FXML相关的问题

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM