这个JavaFX/FXML自定义组件有什么问题?

3

我正在学习为JavaFX 8和Scene Builder编写FXML自定义组件。

我编写了下面的FXML文件,但是Scene Builder无法打开它,给出了“打开操作失败”消息,并由于以下异常而产生:

java.io.IOException: javafx.fxml.LoadException: mycustomcomponent.TicoTeco不是有效类型。
/C:/Users/xxxxx/Documents/NetBeansProjects/MyCustomComponent/src/mycustomcomponent/TicoTeco.fxml:9
    at com.oracle.javafx.scenebuilder.kit.fxom.FXOMLoader.load(FXOMLoader.java:92)
    at com.oracle.javafx.scenebuilder.kit.fxom.FXOMDocument.(FXOMDocument.java:80)
    at com.oracle.javafx.scenebuilder.kit.fxom.FXOMDocument.(FXOMDocument.java:95)
...

为什么会出现此异常?

以下是FXML文件:

<?import java.lang.*?>
<?import java.util.*?>
<?import javafx.scene.*?>
<?import javafx.scene.control.*?>
<?import javafx.scene.layout.*?>

<fx:root type="mycustomcomponent.TicoTeco" prefHeight="93.0" prefWidth="304.0" xmlns="http://javafx.com/javafx/8" xmlns:fx="http://javafx.com/fxml/1">
   <children>
      <BorderPane layoutX="61.0" prefHeight="115.0" prefWidth="200.0">
         <left>
            <Button fx:id="tico" mnemonicParsing="false" text="Tico" BorderPane.alignment="CENTER" />
         </left>
         <right>
            <Button fx:id="teco" mnemonicParsing="false" text="Teco" BorderPane.alignment="CENTER" />
         </right>
      </BorderPane>
   </children>
</fx:root>

以下是TicoTeco.java和Main.java的Java文件:

package mycustomcomponent;

import java.io.IOException;
import javafx.event.ActionEvent;
import javafx.event.EventHandler;
import javafx.fxml.FXML;
import javafx.fxml.FXMLLoader;
import javafx.scene.control.Button;
import javafx.scene.layout.AnchorPane;

public class TicoTeco extends AnchorPane {

    @FXML
    Button tico;

    @FXML
    Button teco;

    public TicoTeco() throws IOException {
        FXMLLoader fxmlLoader = new FXMLLoader(TicoTeco.class.getResource("TicoTeco.fxml"));
        fxmlLoader.setRoot(this);
        fxmlLoader.setController(this);
        fxmlLoader.load();
    }

    @FXML
    public void initialize() {
        final EventHandler<ActionEvent> onAction = 
                event -> System.out.println("Hi, I'm " + (event.getSource() == tico? "Tico" : "Teco") + "!");
        tico.setOnAction(onAction);
        teco.setOnAction(onAction);
    }
}

package mycustomcomponent;

import java.io.IOException;
import javafx.application.Application;
import javafx.event.ActionEvent;
import javafx.event.EventHandler;
import javafx.scene.Scene;
import javafx.scene.control.Button;
import javafx.scene.layout.StackPane;
import javafx.stage.Stage;

public class Main extends Application {

    @Override
    public void start(Stage primaryStage) throws IOException {
        Scene scene = new Scene(new TicoTeco());

        primaryStage.setTitle("Here are Tico and Teco!");
        primaryStage.setScene(scene);
        primaryStage.show();
    }

    /**
     * @param args the command line arguments
     */
    public static void main(String[] args) {
        launch(args);
    }

}

1
你能在fxml中尝试添加导入语句"<?import mycustomcomponent.*?>"吗? 你的代码在eclipse中运行正常。可能是因为NetBeans从src文件夹而不是部署文件夹获取fxml的缘故? - Roland
1
你是否将包含自定义组件的JAR文件导入SceneBuilder中了? - griFlo
@Roland。谢谢你。实际上,导入语句是不必要的,因为我使用了完全限定名称。该代码在NetBeans中也可以工作,但是在Scene Builder中无法工作。为此,type属性必须是AnchorPane,并且自定义组件需要导入到Scene Builder中(这很奇怪,因为我正在开发它)。 - user118967
@griFlo:谢谢。我之前没有做过这个。似乎在开发自定义组件时需要先导入它有点违反直觉! :-) 但是,不幸的是,这似乎就是情况。 - user118967
1个回答

5

这有点棘手。所以你的fxml文件有一点小错误:

你的自定义类扩展了AnchorPane,所以这应该是你fxml文件中的根节点:

<?import java.lang.*?>
<?import java.util.*?>
<?import javafx.scene.*?>
<?import javafx.scene.control.*?>
<?import javafx.scene.layout.*?>

<fx:root type="AnchorPane" prefHeight="93.0" prefWidth="304.0" xmlns="http://javafx.com/javafx/8" xmlns:fx="http://javafx.com/fxml/1">
   <children>
      <BorderPane layoutX="61.0" prefHeight="115.0" prefWidth="200.0">
         <left>
            <Button fx:id="tico" mnemonicParsing="false" text="Tico" BorderPane.alignment="CENTER" />
         </left>
         <right>
            <Button fx:id="teco" mnemonicParsing="false" text="Teco" BorderPane.alignment="CENTER" />
         </right>
      </BorderPane>
   </children>
</fx:root>

在此之后,您需要制作一个jar文件,因为您有一个fxml和一个java类。这是Netbeans中的棘手部分,所以请遵循以下步骤:
第一步:为组件创建一个自己的库项目,其中包含您复制的源文件,如下所示:
第二步:删除已复制的Main(其中包含主方法)文件。
第三步:在项目上执行“清理和构建”。生成的.jar文件将位于项目目录中的子文件夹“dist”中。
第四步:打开Scene Builder并像这样导入CustomComponent .jar文件:
现在,您可以随心所欲地使用该组件。但要注意,对组件的更改不会动态刷新导入的jar文件,您必须重新执行整个过程。

实际上,即使类型是mycustomcomponent.TicoTeco,FXML也可以工作,但是Scene Builder不会像那样导入它。这是否意味着SB仅导入派生自标准JavaFX控件(如AnchorPane)的内容?这很令人失望,因为这意味着自定义组件并没有像标准组件一样得到处理。例如,不能从另一个自定义组件定义自定义组件,对吗? - user118967
所以,这是一种编程范式,你应该在可能的情况下始终使用超类型或接口。因此,即使子子组件源自AnchorPane,真正的组件看起来有所不同,请查看JavaFX源代码,其中要比AnchorPane类更多的内容。 - aw-think

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接