2

我用 JavaFX 创建了一个简单的无边界应用程序。现在我创建了一个标签,它应该替换丢失的“关闭”按钮。实际上,我的尝试不起作用,因为我无法与我的 FXMLController 交互。

我已经尝试将 FXMLController 添加到我的 *.fxml 文件中。我试图将控制器插入 VBox 和 AnchorPane 但两者都不起作用,我真的不明白。

标签关闭测试.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);

             }
        });
    }
}

标签测试.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$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

我希望应用程序启动并且当我按下应用程序将关闭的“X”标签时。

快速提醒:我是 JavaFX 新手,所以我也会感谢每一个 JavaFX 教程推荐。

4

1 回答 1

3

我试图将控制器插入 VBox 和 AnchorPane 但两者都不起作用,我真的不明白。

您提到尝试将fx:controller属性同时放在VBox元素和AnchorPane元素上。您显示的代码是您尝试将其添加到AnchorPane元素中。按原样在我的计算机上运行您的代码会导致LoadException您观察到。这个问题通过将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 -->

注意:我最初注意到另一个问题,即您没有关闭<VBox>or<children>元素。然而,那些结束标签一直都在那里。正如fabian 所指出的,他们只是没有在您的问题中呈现。我忽略了检查这个。


但是,这会导致不同的异常。

// 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

此问题最终是由以下代码引起的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:controllerFXMLLoader创建自己FXMLDocumentController. 正是这种情况下,@FXML注解的字段被注入。另一方面,您创建自己的实例,该实例不会注入字段。您应该使用FXMLDocumentControllerFXMLLoader.

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. 但是,您不会将此方法与 FXML 文件链接,而是将该方法添加EventHandlerLabel. 正确设置,您根本不应该打电话closeLabelPressedLabelTest

您应该做的第一件事是将方法链接到Label通过 FXML 文件。

<!-- 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属性

第二件事是改变closeLabelPressed方法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();
    }
}

Label现在,当按下时调用该方法。在内部,FXMLLoaderonMousePressed属性 (of labelTest) 设置为EventHandler调用closeLabelPressed. 我也改变了System.exit(0)to的使用Platform.exit()。后者将允许 JavaFX 运行时正常关闭(允许您在类似的地方进行清理Application#stop),而前者会立即终止 JVM(并且不会正常返回)。注意:Label可能不再需要注射。

第三件事是删除方法中的closeLabelPressed调用LabelTest.start

在这些更改之后,您的代码应该可以正常工作。


至于请求教程(这在 Stack Overflow 上是题外话,请参阅帮助中心),您可以通过搜索 Google 找到很多。有关“官方”来源,请参阅JavaFX:JavaFX 入门(最近更新为 JavaFX 8)和FXML 简介。Stack Overflow 上也有很多与 FXML 相关的问题。

于 2018-12-26T02:34:30.463 回答