SceneBuilder无法根据给定的属性集创建自定义组件实例。

3

我创建了这个自定义组件:

public class IconButton extends Button {

    @FXML private ImageView imageView;

    private IconButtonState state;
    private String fullIconUrl;
    private String outlineIconUrl;

    public IconButton(@NamedArg("fullIconUrl") String fullIconUrl,
                      @NamedArg("outlineIconUrl") String outlineIconUrl) {
        URL url = getClass().getResource(View.ICON_BUTTON.getFileName());
        FXMLLoader loader = new FXMLLoader(url);

        loader.setRoot(this);
        loader.setController(this);

        state = IconButtonState.NOT_INITIALIZED;

        this.fullIconUrl = fullIconUrl;
        this.outlineIconUrl = outlineIconUrl;

        try {
            loader.load();
        } catch (IOException exception) {
            exception.printStackTrace();
            throw new RuntimeException(exception);
        }
    }

    @FXML
    public void initialize() {
        this.state = IconButtonState.ACTIVE;

        String url = buildUrl(fullIconUrl);
        Image image = new Image(url);
        imageView.setImage(image);
    }
}

<!-- icon-button.fxml -->
<?xml version="1.0" encoding="UTF-8"?>

<?import javafx.scene.control.*?>
<?import javafx.scene.image.*?>

<fx:root mnemonicParsing="false" prefHeight="65.0" prefWidth="98.0" style="-fx-background-color: transparent;" type="Button" xmlns="http://javafx.com/javafx/10.0.2-internal" xmlns:fx="http://javafx.com/fxml/1">
   <graphic>
      <ImageView fx:id="imageView" fitHeight="64.0" fitWidth="48.0" pickOnBounds="true" preserveRatio="true">
      </ImageView>
   </graphic>
</fx:root>

然后,我在另一个 fxml 文件中实例化了我的 IconButton 组件,代码如下:

<!-- home.fxml -->
<?xml version="1.0" encoding="UTF-8"?>

<?import javafx.geometry.*?>
<?import javafx.scene.layout.*?>
<?import agill.deshopp.components.*?>

<BorderPane maxHeight="-Infinity" maxWidth="-Infinity" minHeight="-Infinity" minWidth="-Infinity" prefHeight="768.0" prefWidth="1024.0" xmlns="http://javafx.com/javafx/10.0.2-internal" xmlns:fx="http://javafx.com/fxml/1">
    <top>
        <HBox styleClass="app-bar" stylesheets="@../css/home.css">
         <padding>
            <Insets bottom="15.0" top="15.0" />
         </padding>
         <children>
             <IconButton fullIconUrl="form-full" outlineIconUrl="form-outline"></IconButton>
             <IconButton fullIconUrl="message-full" outlineIconUrl="message-outline"></IconButton>
             <IconButton fullIconUrl="chart-full" outlineIconUrl="chart-outline"></IconButton>
             <Region HBox.hgrow="ALWAYS"></Region>
             <IconButton fullIconUrl="settings-full" outlineIconUrl="settings-outline"></IconButton>
         </children>
        </HBox>
    </top>
   <center>
      <Pane BorderPane.alignment="CENTER" />
   </center>
</BorderPane>

代码运行良好,屏幕渲染也符合预期。然而,我无法在SceneBuilder中打开该文件,它提示了以下异常:
java.lang.RuntimeException: Cannot create instance of agill.deshopp.components.IconButton with given set of properties: [fullIconUrl, outlineIconUrl]

javafx.fxml.LoadException: 
/home/allan/IdeaProjects/california/src/main/resources/agill/deshopp/fxml/home.fxml:14

我该如何修复这个问题?


3
您从未实例化fullIconUrloutlineIconUrl。假定您阅读完整的堆栈跟踪,您将在此行this.fullIconUrl.set(fullIconUrl);看到一个空指针异常。请注意,翻译保持原意并尽可能简洁易懂。 - James_D
2
这里的道德是你应该始终阅读完整的堆栈跟踪 - James_D
1
嗯。当然,所有这些都是通过反射发生的,因此异常的根本原因可能会“丢失”。我回到电脑时会重新打开,因为在初始化这些字段时仍然会出现问题。(手机应用程序没有重新打开的选项。) - James_D
这只是一种根据我认为曾经阅读过的信息所做出的猜测,但如果在调用loader.load()之前调用loader.setClassLoader(getClass().getClassLoader())会发生什么? - Slaw
请发布 [mre] - c0der
显示剩余5条评论
2个回答

1
问题中发布的代码不是mre,无法调用。
但以下代码是mre。它可以正常运行而不会出现异常。
可以使用ScreenBuilder编辑Fxml文件。
将其修改为您需要的内容,以查找问题中发布的代码中的错误。
请注意,form-full会抛出异常,因此我使用了form_full,并且fxml分配应包括$符号:fullIconUrl="$form_full"
package fx_tests.test;
import java.io.IOException;
import java.net.URL;
import javafx.beans.NamedArg;
import javafx.fxml.FXML;
import javafx.fxml.FXMLLoader;
import javafx.scene.control.Button;
import javafx.scene.image.Image;
import javafx.scene.image.ImageView;

public class IconButton extends Button {

    @FXML private ImageView imageView;
    private final String fullIconUrl;

    public IconButton(@NamedArg("fullIconUrl") String fullIconUrl) {

        this.fullIconUrl = fullIconUrl;

        URL url = getClass().getResource("icon-button.fxml");
        FXMLLoader loader = new FXMLLoader(url);
        loader.setRoot(this);
        loader.setController(this);
    
        try {
            loader.load();
        } catch (IOException exception) {
            exception.printStackTrace();
        }
    }

    @FXML
    public void initialize() {
        Image image = new Image(fullIconUrl);
        imageView.setImage(image);
    }
}

Home.fxml:

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

<?import javafx.geometry.*?>
<?import javafx.scene.layout.*?>
<?import fx_tests.test.*?>

<BorderPane maxHeight="-Infinity" maxWidth="-Infinity" minHeight="-Infinity" minWidth="-Infinity" prefHeight="168.0" 
prefWidth="124.0" xmlns="http://javafx.com/javafx/10.0.2-internal" xmlns:fx="http://javafx.com/fxml/1">
    <top>
         <HBox>
         <padding>
            <Insets bottom="15.0" top="15.0" />
         </padding>
         <children>
             <IconButton fullIconUrl="$form_full"></IconButton>
         </children>
        </HBox>
    </top>
   <center>
      <Pane BorderPane.alignment="CENTER" />
   </center>
</BorderPane>

icon-button.fxml:

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

<?import javafx.scene.control.*?>
<?import javafx.scene.image.*?>

<fx:root mnemonicParsing="false" prefHeight="65.0" prefWidth="98.0" style="-fx-background-color: transparent;" 
type="Button" xmlns="http://javafx.com/javafx/10.0.2-internal" xmlns:fx="http://javafx.com/fxml/1">
   <graphic>
      <ImageView fx:id="imageView" fitHeight="64.0" fitWidth="48.0" pickOnBounds="true" preserveRatio="true">
      </ImageView>
   </graphic>
</fx:root>

测试使用:

import javafx.application.Application;
import javafx.fxml.FXMLLoader;
import javafx.scene.Parent;
import javafx.scene.Scene;
import javafx.stage.Stage;
    
public class Main extends Application {

    private static final String IMAGE = "https://www.shareicon.net/data/128x128/2015/03/28/14104_animal_256x256.png";
    @Override
    public void start(Stage currentStage) throws Exception {

        FXMLLoader loader = new FXMLLoader(getClass().getResource("Home.fxml"));
        loader.getNamespace().put("form_full", IMAGE);
        Parent root=loader.load();
        currentStage.setScene(new Scene(root));
        currentStage.show();
    }

    public static void main(String[] args) {
        launch(args);
    }
}

0

当您在JavaFX中创建自定义组件时,SceneBuilder需要知道此组件的定义以便加载它。我有一些自己的自定义组件,必须将它们导出为jar文件,并导入到SceneBuilder中。以下是一个提供如何执行此操作的答案:

将自定义组件添加到SceneBuilder 2.0

有一个注意点。SceneBuilder仅支持Java 11及以下版本。因此,您的自定义组件必须使用此版本构建并导出为jar文件。我在Java 14上遇到了问题,不得不将我的特定JavaFX组件回退到Java 11。祝好运!


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