【问题标题】:Why does FXMLLoader not initialize fields when a field is declared as a super-type为什么当字段声明为超类型时,FXMLLoader 不初始化字段
【发布时间】:2021-03-04 16:02:42
【问题描述】:

当我声明一个现场播放器时,如下所示:

class Controller{
    @FXML Shape player;
}

.fxml 文件 - <Rectangle fx:id="player" ...\>
其中Controllerplayer被声明为超类型(Shape),在fxml文件中 它被声明为子类型。

我将播放器声明为 Shape,而不是 Rectangle,因为我有多个类似的 fxml 文件,并且程序会在运行时决定加载哪个文件。每个 fxml 文件都有一个 Shape 子类的播放器对象

我的问题是,当一个字段被声明为超类型时,fxml 加载器不会初始化该字段。我想知道这个问题的解决方法。

一个最小可重现的例子:

import javafx.application.Application;
import javafx.fxml.FXML;
import javafx.fxml.FXMLLoader;
import javafx.scene.Scene;
import javafx.scene.shape.Shape;
import javafx.stage.Stage;

public class test2 extends Application {
    @FXML Shape player;
    public void start(Stage stage) throws Exception
    {
        Scene scene = new Scene(
                FXMLLoader.load(getClass().getResource("t.fxml"))
        );
        stage.setTitle("JavaFX Example");
        stage.setScene(scene);
        stage.show();
        System.out.println(player); //prints null
    }
    public static void main (String [] args){
        launch(args);
    }
}
<?xml version="1.0" encoding="UTF-8"?>

<?import javafx.scene.layout.Pane?>
<?import javafx.scene.shape.Rectangle?>
<Pane xmlns:fx="http://javafx.com/fxml" prefHeight="400.0" prefWidth="600.0">
    <Rectangle fx:id="player" x="20" y="20" width="40" height="40"/>
</Pane>

【问题讨论】:

  • 该字段在控制器中初始化,而不是在Applicaation 实例中。你甚至没有在 FXML 中声明一个控制器类。
  • 我的问题是,当一个字段被声明为超类型时,fxml 加载器不会初始化该字段你确定吗?如果定义为Rectangle player,是否初始化?
  • 另外,Controller 不是您在问题中发布的任何内容的超类型...
  • 对不起。这不是因为使用了超类型。但我无法初始化程序中的字段。找到原因后我会编辑问题
  • 另外:“我有多个类似的 fxml 文件,程序在运行时决定加载哪个文件”。每个 FXML 文件应该有一个不同的控制器类。

标签: java javafx fxml


【解决方案1】:

@FXML-annotated 字段在控制器中初始化。通常,要创建控制器,您需要在 FXML 的根元素中指定 fx:controller 属性(尽管还有其他方法可以做到这一点)。您的 test2 类 [原文如此] 不是控制器类(即使是,调用 start() 的实例也不是控制器)。

您的代码的以下修改表明声明为超类类型的字段确实按照您的预期进行了初始化:

Controller.java:

package org.jamesd.examples.supertype;

import javafx.fxml.FXML;
import javafx.scene.shape.Shape;

public class Controller{
    @FXML Shape player;
    
    public void initialize() {
        System.out.println(player);
    }
}

t.fxml(注意fx:controller属性):

<?xml version="1.0" encoding="UTF-8"?>

<?import javafx.scene.layout.Pane?>
<?import javafx.scene.shape.Rectangle?>
<Pane xmlns:fx="http://javafx.com/fxml" prefHeight="400.0"
    prefWidth="600.0"
    fx:controller="org.jamesd.examples.supertype.Controller">
    <Rectangle fx:id="player" x="20" y="20" width="40" height="40" />
</Pane>

Test2.java:

package org.jamesd.examples.supertype;

import javafx.application.Application;
import javafx.fxml.FXML;
import javafx.fxml.FXMLLoader;
import javafx.scene.Scene;
import javafx.scene.shape.Shape;
import javafx.stage.Stage;

public class Test2 extends Application {
    @FXML Shape player;
    public void start(Stage stage) throws Exception
    {
        Scene scene = new Scene(
                FXMLLoader.load(getClass().getResource("t.fxml"))
        );
        stage.setTitle("JavaFX Example");
        stage.setScene(scene);
        stage.show();
    }
    public static void main (String [] args){
        launch(args);
    }
}

这会产生预期的输出:

Rectangle[id=player, x=20.0, y=20.0, width=40.0, height=40.0, fill=0x000000ff]

【讨论】:

  • "即使是这样,调用 start() 的实例也不会是控制器" - 请您解释一下。如果我在 start() 方法中写FXMLLoader loader = ...; loader.setController(this) 行,'the' test2 Object 会成为控制器吗?
  • @febot 是的,这会将当前对象指定为控制器。但是使用fx:controller 属性是行不通的。 (无论哪种方式都是糟糕的设计。Application 类应该管理应用程序生命周期,而不是充当控制器,因此您违反了单一责任原则。)
猜你喜欢
  • 2020-05-01
  • 2020-05-01
  • 2019-06-13
  • 1970-01-01
  • 2017-06-26
  • 2015-04-04
  • 2011-02-02
  • 2018-04-06
  • 1970-01-01
相关资源
最近更新 更多