JavaFX 自定义节点不呈现子节点

Posted

技术标签:

【中文标题】JavaFX 自定义节点不呈现子节点【英文标题】:JavaFX custom node not rendering children 【发布时间】:2021-07-25 22:11:42 【问题描述】:

我创建了一个自定义组件,它基本上是一个包含更多组件的 vbox。但是,当我将它包含在 HomeView.fxml 中时,组件的子项无处可见。每个单独的内部组件都是 null,导致 HomeController 上的绑定部分出现 NullPointerException。我错过了什么?

HomeView.fxml

<StackPane xmlns="http://javafx.com/javafx/11.0.1" xmlns:fx="http://javafx.com/fxml/1" fx:controller="views.home.HomeController">
    <BorderPane>
        <center>Placeholder</center>
    </BorderPane>
    <SidePane fx:id="sidePane" maxWidth="200.0" prefWidth="200.0" style="-fx-background-color: cyan;" translateX="200.0" StackPane.alignment="CENTER_RIGHT" />
</StackPane>

HomeController.java

public class HomeController implements Initializable 

    private final HomeViewModel viewModel;

    @FXML private SidePane sidePane;

    public HomeController(HomeViewModel viewModel) 
        this.viewModel = viewModel;
    

    @Override
    public void initialize(URL url, ResourceBundle resourceBundle) 
        // Bind side pane - NullPointerException here, that's why it is commented out
        // sidePane.imageProperty().bindBidirectional(viewModel.selectedPlatform().image());
        // sidePane.nameProperty().bindBidirectional(viewModel.selectedPlatform().name());
        // sidePane.identifierProperty().bind(viewModel.selectedPlatform().id().asString());
    


SidePane.fxml

<fx:root type="javafx.scene.layout.Region"
         fx:controller="viewscomponents.sidepane.SidePane"
     xmlns="http://javafx.com/javafx"
     xmlns:fx="http://javafx.com/fxml"
     prefWidth="200" maxWidth="200">
    <childrenUnmodifiable>
        <VBox>
            <ImageView fx:id="image" />
            <Label text="Name:" />
            <TextField fx:id="name" />
            <HBox>
                <Label alignment="BOTTOM_RIGHT" text="id: " />
                <Label fx:id="id" />
            </HBox>
        </VBox>
    </childrenUnmodifiable>
</fx:root>

SidePane.java

public class SidePane extends Region 

    @FXML private ImageView image;
    @FXML private TextField name;
    @FXML private Label id;

    public ObjectProperty<Image> imageProperty() 
        return image.imageProperty();
    

    public StringProperty nameProperty() 
        return name.textProperty();
    

    public StringProperty identifierProperty() 
        return id.textProperty();
    


【问题讨论】:

minimal reproducible example 请.. 包括导入和应用 你永远不会在任何地方加载SidePane.fxml。在documentation 中查看此模式的示例 【参考方案1】:

正如 James_D 在他的评论中所说,您永远不会加载自定义控件的 fxml 文件。另外我认为它不适用于Region,您不应该在 fxml 文件中指定控制器。它应该像这样工作:

SidePane.java

import javafx.beans.property.ObjectProperty;
import javafx.beans.property.StringProperty;
import javafx.fxml.FXML;
import javafx.fxml.FXMLLoader;
import javafx.scene.control.Label;
import javafx.scene.control.TextField;
import javafx.scene.image.Image;
import javafx.scene.image.ImageView;
import javafx.scene.layout.VBox;

import java.io.IOException;

public class SidePane extends VBox 

    @FXML
    private ImageView image;
    @FXML
    private TextField name;
    @FXML
    private Label id;

    public SidePane() throws IOException 
        FXMLLoader loader = new FXMLLoader(App.class.getResource("SidePane.fxml"));
        loader.setRoot(this);
        loader.setController(this);
        loader.load();
    

    public ObjectProperty<Image> imageProperty() 
        return image.imageProperty();
    

    public StringProperty nameProperty() 
        return name.textProperty();
    

    public StringProperty identifierProperty() 
        return id.textProperty();
    

SidePane.fxml

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

<?import javafx.scene.control.Label?>
<?import javafx.scene.control.TextField?>
<?import javafx.scene.image.ImageView?>
<?import javafx.scene.layout.HBox?>
<?import javafx.scene.layout.VBox?>

<fx:root type="VBox"
         xmlns="http://javafx.com/javafx"
         xmlns:fx="http://javafx.com/fxml"
         prefWidth="200" maxWidth="200">
    <ImageView fx:id="image"/>
    <Label text="Name:"/>
    <TextField fx:id="name"/>
    <HBox>
        <Label alignment="BOTTOM_RIGHT" text="id: "/>
        <Label fx:id="id"/>
    </HBox>
</fx:root>

【讨论】:

但是SidePane正在暴露Vbox方法,这是常见的做法吗?还是有其他选择? 你可以把VBox放在一个Region中,然后让SidePane的类型为Region。这将隐藏 VBox 方法。 @DaveB 你能创建一个使用Region 的例子并与我们分享吗?

以上是关于JavaFX 自定义节点不呈现子节点的主要内容,如果未能解决你的问题,请参考以下文章

带有自定义 CellFactory 的 ListView 修剪不可见节点

在JavaFX中添加自定义组件

当我取消选中自定义树视图中的子节点复选框时,如何取消选中所有父节点

如何使用子节点创建自定义 React Native 组件

自定义 SCNGeometry 节点上基于物理的照明

JavaFx SceneBuilder 2.0 无法解析自定义控件