首页 文章

使用自定义控件加载FXML时,SceneBuilder显示布局失败(“null”)

提问于
浏览
0

我正在尝试在SceneBuilder中使用自定义控件 . 我构建的是一个名为ContentSection的控件 . 为了解决创建自定义控件的可能性,我nealry复制了一个 Headers 窗格的代码,并对皮肤进行了一些更改,以匹配我对ContentSection的未来要求 .

我的控件在我的应用程序中工作正常,但我无法在SceneBuilder中加载它 .

这是我的控件代码:

import java.util.ArrayList;
import java.util.Collections;
import java.util.List;

import de.jensd.fx.glyphs.fontawesome.FontAwesomeIcon;
import javafx.beans.DefaultProperty;
import javafx.beans.property.BooleanProperty;
import javafx.beans.property.BooleanPropertyBase;
import javafx.beans.property.DoubleProperty;
import javafx.beans.property.ObjectProperty;
import javafx.beans.property.SimpleDoubleProperty;
import javafx.beans.property.SimpleObjectProperty;
import javafx.beans.property.StringProperty;
import javafx.beans.property.StringPropertyBase;
import javafx.css.CssMetaData;
import javafx.css.PseudoClass;
import javafx.css.StyleConverter;
import javafx.css.Styleable;
import javafx.css.StyleableBooleanProperty;
import javafx.css.StyleableObjectProperty;
import javafx.css.StyleableProperty;
import javafx.geometry.Orientation;
import javafx.scene.AccessibleAction;
import javafx.scene.AccessibleAttribute;
import javafx.scene.Node;
import javafx.scene.control.Control;
import javafx.scene.control.Skin;
import javafx.util.Duration;

@DefaultProperty("content")
public class ContentSection extends Control {

    public ContentSection() {
        getStyleClass().setAll(DEFAULT_STYLE_CLASS);

        // initialize pseudo-class state
        pseudoClassStateChanged(PSEUDO_CLASS_EXPANDED, true);
        pseudoClassStateChanged(PSEUDO_CLASS_COLLAPSED, false);
    }

    public ContentSection(final String title, final Node content) {
        this();
        setTitle(title);
        setContent(content);
    }

    private final StringProperty titleProperty = new StringPropertyBase() {
        @Override
        protected void invalidated() {
            notifyAccessibleAttributeChanged(AccessibleAttribute.TEXT);
        }

        @Override
        public Object getBean() {
            return ContentSection.this;
        }

        @Override
        public String getName() {
            return "title";
        };
    };

    public final void setTitle(final String value) {
        titleProperty().set(value);
    }

    public final String getTitle() {
        return titleProperty == null ? null : titleProperty.get();
    }

    public final StringProperty titleProperty() {
        return titleProperty;
    }

    private DoubleProperty iconSizeProperty;

    public final void setIconSize(final double value) {
        iconSizeProperty().set(value);
    }

    public final double getIconSize() {
        return iconSizeProperty == null ? -1 : iconSizeProperty.get();
    }

    public final DoubleProperty iconSizeProperty() {
        if (iconSizeProperty == null) {
            iconSizeProperty = new SimpleDoubleProperty(this, "iconSize", -1);
        }
        return iconSizeProperty;
    }

    private ObjectProperty<FontAwesomeIcon> iconProperty;

    public final void setIcon(final FontAwesomeIcon value) {
        iconProperty().set(value);
    }

    public final FontAwesomeIcon getIcon() {
        return iconProperty == null ? null : iconProperty.get();
    }

    @Override
    public String getUserAgentStylesheet() {
        return ContentSection.class.getClassLoader().getResource("content-section.css").toExternalForm();
    }

    public final ObjectProperty<FontAwesomeIcon> iconProperty() {
        if (iconProperty == null) {
            iconProperty = new SimpleObjectProperty<>(this, "icon");
        }
        return iconProperty;
    }

    private ObjectProperty<Node> contentProperty;

    public final void setContent(final Node value) {
        contentProperty().set(value);
    }

    public final Node getContent() {
        return contentProperty == null ? null : contentProperty.get();
    }

    public final ObjectProperty<Node> contentProperty() {
        if (contentProperty == null) {
            contentProperty = new SimpleObjectProperty<Node>(this, "content");
        }
        return contentProperty;
    }

    private final BooleanProperty expandedProperty = new BooleanPropertyBase(true) {
        @Override
        protected void invalidated() {
            final boolean active = get();
            pseudoClassStateChanged(PSEUDO_CLASS_EXPANDED, active);
            pseudoClassStateChanged(PSEUDO_CLASS_COLLAPSED, !active);
            notifyAccessibleAttributeChanged(AccessibleAttribute.EXPANDED);
        }

        @Override
        public Object getBean() {
            return ContentSection.this;
        }

        @Override
        public String getName() {
            return "expanded";
        }
    };

    public final void setExpanded(final boolean value) {
        expandedProperty().set(value);
    }

    public final boolean isExpanded() {
        return expandedProperty.get();
    }

    public final BooleanProperty expandedProperty() {
        return expandedProperty;
    }

    private final BooleanProperty animatedProperty = new StyleableBooleanProperty(true) {

        @Override
        public Object getBean() {
            return ContentSection.this;
        }

        @Override
        public String getName() {
            return "animated";
        }

        @Override
        public CssMetaData<ContentSection, Boolean> getCssMetaData() {
            return StyleableProperties.ANIMATED;
        }

    };

    public final void setAnimated(final boolean value) {
        animatedProperty().set(value);
    }

    public final boolean isAnimated() {
        return animatedProperty.get();
    }

    public final BooleanProperty animatedProperty() {
        return animatedProperty;
    }

    private final ObjectProperty<Duration> animationDurationProperty = new SimpleObjectProperty<>(this, "animationDuration", Duration.millis(350.0));

    public final void setAnimationDuration(final Duration value) {
        animationDurationProperty().set(value);
    }

    public final Duration getAnimationDuration() {
        return animationDurationProperty().get();
    }

    public final ObjectProperty<Duration> animationDurationProperty() {
        return animationDurationProperty;
    }

    private final BooleanProperty collapsibleProperty = new StyleableBooleanProperty(true) {

        @Override
        public Object getBean() {
            return ContentSection.this;
        }

        @Override
        public String getName() {
            return "collapsible";
        }

        @Override
        public CssMetaData<ContentSection, Boolean> getCssMetaData() {
            return StyleableProperties.COLLAPSIBLE;
        }

    };

    public final void setCollapsible(final boolean value) {
        collapsibleProperty().set(value);
    }

    public final boolean isCollapsible() {
        return collapsibleProperty.get();
    }

    public final BooleanProperty collapsibleProperty() {
        return collapsibleProperty;
    }

    private final ObjectProperty<Number> headerSizeProperty = new StyleableObjectProperty<Number>(44.0) {

        @Override
        public String getName() {
            return "headerSize";
        }

        @Override
        public Object getBean() {
            return ContentSection.this;
        }

        @Override
        public CssMetaData<? extends Styleable, Number> getCssMetaData() {
            return StyleableProperties.HEADER_SIZE;
        }
    };

    public final void setHeaderSize(final Number value) {
        headerSizeProperty().set(value);
    }

    public final Number getHeaderSize() {
        return headerSizeProperty().get();
    }

    public final ObjectProperty<Number> headerSizeProperty() {
        return headerSizeProperty;
    }

    /** {@inheritDoc} */
    @Override
    protected Skin<?> createDefaultSkin() {
        return new ContentSectionSkin(this);
    }

    private static final String DEFAULT_STYLE_CLASS = "content-section";

    private static final PseudoClass PSEUDO_CLASS_EXPANDED = PseudoClass.getPseudoClass("expanded");
    private static final PseudoClass PSEUDO_CLASS_COLLAPSED = PseudoClass.getPseudoClass("collapsed");

    private static class StyleableProperties {

        private static final CssMetaData<ContentSection, Boolean> COLLAPSIBLE =
                new CssMetaData<ContentSection, Boolean>("-fx-collapsible", StyleConverter.getBooleanConverter(), Boolean.TRUE) {

                    @Override
                    public boolean isSettable(final ContentSection n) {
                        return n.collapsibleProperty == null || !n.collapsibleProperty.isBound();
                    }

                    @Override
                    public StyleableProperty<Boolean> getStyleableProperty(final ContentSection n) {
                        return (StyleableProperty<Boolean>) n.collapsibleProperty();
                    }
                };

        private static final CssMetaData<ContentSection, Number> HEADER_SIZE =
                new CssMetaData<ContentSection, Number>("-fx-header-size", StyleConverter.getSizeConverter(), 44.0) {

                    @Override
                    public boolean isSettable(final ContentSection n) {
                        return n.collapsibleProperty == null || !n.collapsibleProperty.isBound();
                    }

                    @Override
                    public StyleableProperty<Number> getStyleableProperty(final ContentSection n) {
                        return (StyleableProperty<Number>) n.headerSizeProperty();
                    }
                };

        private static final CssMetaData<ContentSection, Boolean> ANIMATED =
                new CssMetaData<ContentSection, Boolean>("-fx-animated", StyleConverter.getBooleanConverter(), Boolean.TRUE) {

                    @Override
                    public boolean isSettable(final ContentSection n) {
                        return n.animatedProperty == null || !n.animatedProperty.isBound();
                    }

                    @Override
                    public StyleableProperty<Boolean> getStyleableProperty(final ContentSection n) {
                        return (StyleableProperty<Boolean>) n.animatedProperty();
                    }
                };

        private static final List<CssMetaData<? extends Styleable, ?>> STYLEABLES;
        static {
            final List<CssMetaData<? extends Styleable, ?>> styleables = new ArrayList<CssMetaData<? extends Styleable, ?>>(Control.getClassCssMetaData());
            styleables.add(COLLAPSIBLE);
            styleables.add(ANIMATED);
            styleables.add(HEADER_SIZE);
            STYLEABLES = Collections.unmodifiableList(styleables);
        }
    }

    public static List<CssMetaData<? extends Styleable, ?>> getClassCssMetaData() {
        return StyleableProperties.STYLEABLES;
    }

    @Override
    public List<CssMetaData<? extends Styleable, ?>> getControlCssMetaData() {
        return getClassCssMetaData();
    }

    @Override
    public Orientation getContentBias() {
        final Node c = getContent();
        return c == null ? super.getContentBias() : c.getContentBias();
    }

    @Override
    public Object queryAccessibleAttribute(final AccessibleAttribute attribute, final Object... parameters) {
        switch (attribute) {
            case TEXT: {
                final String accText = getAccessibleText();
                if (accText != null && !accText.isEmpty()) {
                    return accText;
                }
                return getTitle();
            }
            case EXPANDED:
                return isExpanded();
            default:
                return super.queryAccessibleAttribute(attribute, parameters);
        }
    }

    @Override
    public void executeAccessibleAction(final AccessibleAction action, final Object... parameters) {
        switch (action) {
            case EXPAND:
                setExpanded(true);
                break;
            case COLLAPSE:
                setExpanded(false);
                break;
            default:
                super.executeAccessibleAction(action);
        }
    }

}

这是我的皮肤代码:

import com.sun.javafx.scene.control.skin.BehaviorSkinBase;

import de.jensd.fx.glyphs.fontawesome.FontAwesomeIcon;
import de.jensd.fx.glyphs.fontawesome.FontAwesomeIconView;
import javafx.animation.Animation.Status;
import javafx.animation.Interpolator;
import javafx.animation.KeyFrame;
import javafx.animation.KeyValue;
import javafx.animation.Timeline;
import javafx.beans.property.DoubleProperty;
import javafx.beans.property.SimpleDoubleProperty;
import javafx.collections.ListChangeListener;
import javafx.scene.Node;
import javafx.scene.control.Button;
import javafx.scene.control.Label;
import javafx.scene.layout.BorderPane;
import javafx.scene.layout.HBox;
import javafx.scene.layout.Pane;
import javafx.scene.layout.Priority;
import javafx.scene.layout.StackPane;
import javafx.util.Duration;

@SuppressWarnings("restriction")
public class ContentSectionSkin extends BehaviorSkinBase<ContentSection, ContentSectionBehavior> {

    private final BorderPane container;
    private final HBox header;
    private final BorderPane contentContainer;

    private Timeline timeline;
    private double transitionStartValue;

    private DoubleProperty transition;

    private FontAwesomeIconView expandCollapseIcon;

    private Button expandCollapseButton;

    private HBox sectionToolBar;

    public ContentSectionSkin(final ContentSection contentSection) {
        super(contentSection, new ContentSectionBehavior(contentSection));
        transitionStartValue = 0;

        container = createContainer();
        getChildren().setAll(container);

        header = createHeader();
        container.setTop(header);

        contentContainer = createContentContainer();
        container.setCenter(contentContainer);

        registerChangeListener(contentSection.contentProperty(), "CONTENT");
        registerChangeListener(contentSection.expandedProperty(), "EXPANDED");
        registerChangeListener(contentSection.collapsibleProperty(), "COLLAPSIBLE");

        sectionToolBar.getChildren().addListener((ListChangeListener<Node>) c -> updateHeader());

        if (contentSection.isExpanded()) {
            setTransition(1.0f);
            setExpanded(contentSection.isExpanded());
        } else {
            setTransition(0.0f);
            if (getSkinnable().getContent() != null) {
                getSkinnable().getContent().setVisible(false);
            }
        }
    }

    @Override
    protected double computeMaxWidth(final double height, final double topInset, final double rightInset, final double bottomInset, final double leftInset) {
        return Double.MAX_VALUE;
    }

    @Override
    protected double computeMinHeight(final double width, final double topInset, final double rightInset, final double bottomInset, final double leftInset) {
        final double headerHeight = snapSize(header.prefHeight(width));
        final double contentHeight = contentContainer.minHeight(width) * getTransition();
        final double minHeight = headerHeight + snapSize(contentHeight) + topInset + bottomInset;
        return minHeight;
    }

    @Override
    protected double computeMinWidth(final double height, final double topInset, final double rightInset, final double bottomInset, final double leftInset) {
        final double headerWidth = snapSize(header.prefWidth(height));
        final double contentWidth = snapSize(contentContainer.minWidth(height));
        final double minWidth = Math.max(headerWidth, contentWidth) + leftInset + rightInset;
        return minWidth;
    }

    @Override
    protected double computePrefHeight(final double width, final double topInset, final double rightInset, final double bottomInset, final double leftInset) {
        final double headerHeight = snapSize(header.prefHeight(width));
        final double contentHeight = contentContainer.prefHeight(width) * getTransition();
        final double prefHeight = headerHeight + snapSize(contentHeight) + topInset + bottomInset;
        return prefHeight;
    }

    @Override
    protected double computePrefWidth(final double height, final double topInset, final double rightInset, final double bottomInset, final double leftInset) {
        final double headerWidth = snapSize(header.prefWidth(height));
        final double contentWidth = snapSize(contentContainer.prefWidth(height));
        final double prefWidth = Math.max(headerWidth, contentWidth) + leftInset + rightInset;
        return prefWidth;
    }

    private BorderPane createContainer() {
        final BorderPane container = new BorderPane();
        container.setMinSize(0.0, 0.0);
        return container;
    }

    private BorderPane createContentContainer() {
        final BorderPane contentContainer = new BorderPane();
        contentContainer.getStyleClass().setAll("content");
        contentContainer.setMinSize(0.0, 0.0);

        if (getSkinnable().getContent() != null) {
            contentContainer.setCenter(getSkinnable().getContent());
        }

        return contentContainer;
    }

    private Button createExpandCollapseButton() {
        final Button expandCollapseButton = new Button();
        expandCollapseButton.setMaxSize(Double.MAX_VALUE, Double.MAX_VALUE);

        expandCollapseButton.setOnAction(event -> {
            getSkinnable().setExpanded(!getSkinnable().isExpanded());
        });

        final StackPane expandCollapseIconContainer = new StackPane();
        expandCollapseIconContainer.getStyleClass().setAll("icon-container");
        expandCollapseIconContainer.setId("last");
        expandCollapseIconContainer.setMaxSize(Double.MAX_VALUE, Double.MAX_VALUE);
        expandCollapseIconContainer.setPrefSize(21, 21);
        expandCollapseIconContainer.setMinSize(21, 21);
        expandCollapseButton.setGraphic(expandCollapseIconContainer);

        expandCollapseIcon = new FontAwesomeIconView(FontAwesomeIcon.CHEVRON_DOWN);
        expandCollapseIcon.setStyleClass("icon");
        expandCollapseIconContainer.getChildren().add(expandCollapseIcon);
        return expandCollapseButton;
    }

    private HBox createHeader() {
        final HBox header = new HBox();
        header.getStyleClass().setAll("header");
        header.setPrefHeight(getSkinnable().getHeaderSize().doubleValue());
        header.setMaxSize(Double.MAX_VALUE, Double.MAX_VALUE);

        header.getChildren().add(createIconContainer());
        header.getChildren().add(createTitleLabel());
        header.getChildren().add(createPlaceholder());
        header.getChildren().add(createSectionToolBar());

        return header;
    }

    private FontAwesomeIconView createIcon() {
        final FontAwesomeIconView iconView = new FontAwesomeIconView();
        iconView.setStyleClass("icon");
        iconView.setIcon(getSkinnable().getIcon());
        iconView.glyphSizeProperty().bind(getSkinnable().iconSizeProperty());
        return iconView;
    }

    private StackPane createIconContainer() {
        final StackPane iconContainer = new StackPane();
        iconContainer.getStyleClass().setAll("icon-container");
        iconContainer.setPrefSize(50, 50);
        iconContainer.getChildren().add(createIcon());
        return iconContainer;
    }

    private Pane createPlaceholder() {
        final Pane placeholder = new Pane();
        HBox.setHgrow(placeholder, Priority.ALWAYS);
        return placeholder;
    }

    private HBox createSectionToolBar() {
        sectionToolBar = new HBox();
        sectionToolBar.getStyleClass().setAll("section-tool-bar");
        expandCollapseButton = createExpandCollapseButton();
        if (getSkinnable().isCollapsible()) {
            sectionToolBar.getChildren().add(sectionToolBar.getChildren().size(), expandCollapseButton);
        }
        return sectionToolBar;
    }

    private Label createTitleLabel() {
        final Label titleLabel = new Label();
        titleLabel.setMaxSize(Double.MAX_VALUE, Double.MAX_VALUE);
        titleLabel.textProperty().bind(getSkinnable().titleProperty());
        return titleLabel;
    }

    private void disableCache() {
        getSkinnable().getContent().setCache(false);
    }

    private void doAnimationTransition() {
        if (getSkinnable().getContent() == null) {
            return;
        }

        Duration duration;
        if (timeline != null && (timeline.getStatus() != Status.STOPPED)) {
            duration = timeline.getCurrentTime();
            timeline.stop();
        } else {
            duration = getSkinnable().getAnimationDuration();
        }

        timeline = new Timeline();
        timeline.setCycleCount(1);

        KeyFrame k1, k2;

        if (getSkinnable().isExpanded()) {
            k1 = new KeyFrame(Duration.ZERO, event -> {
                // start expand
                getSkinnable().getContent().setVisible(true);
            }, new KeyValue(transitionProperty(), transitionStartValue));

            k2 = new KeyFrame(duration, event -> {
                // end expand
            }, new KeyValue(transitionProperty(), 1, Interpolator.LINEAR)

            );
        } else {
            k1 = new KeyFrame(Duration.ZERO, event -> {
                // Start collapse
            }, new KeyValue(transitionProperty(), transitionStartValue));

            k2 = new KeyFrame(duration, event -> {
                // end collapse
                getSkinnable().getContent().setVisible(false);
            }, new KeyValue(transitionProperty(), 0, Interpolator.LINEAR));
        }

        timeline.getKeyFrames().setAll(k1, k2);
        timeline.play();
    }

    private void enableCache() {
        getSkinnable().getContent().setCache(true);
    }

    public BorderPane getContentContainer() {
        return contentContainer;
    }

    private double getTransition() {
        return transition == null ? 0.0 : transition.get();
    }

    @Override
    protected void handleControlPropertyChanged(final String property) {
        super.handleControlPropertyChanged(property);
        if ("CONTENT".equals(property)) {
            final Node content = getSkinnable().getContent();
            if (content == null) {
                contentContainer.setCenter(null);
            } else {
                contentContainer.setCenter(content);
            }
        } else if ("EXPANDED".equals(property)) {
            setExpanded(getSkinnable().isExpanded());
        } else if ("COLLAPSIBLE".equals(property)) {
            updateHeader();
        }
    }

    private void setExpanded(final boolean expanded) {
        if (!getSkinnable().isCollapsible()) {
            setTransition(1.0f);
            return;
        }

        // we need to perform the transition between expanded / hidden
        if (getSkinnable().isAnimated()) {
            transitionStartValue = getTransition();
            doAnimationTransition();
        } else {
            if (expanded) {
                setTransition(1.0f);
            } else {
                setTransition(0.0f);
            }
            if (getSkinnable().getContent() != null) {
                getSkinnable().getContent().setVisible(expanded);
            }
            getSkinnable().requestLayout();
        }
    }

    private void setTransition(final double value) {
        transitionProperty().set(value);
    }

    private DoubleProperty transitionProperty() {
        if (transition == null) {
            transition = new SimpleDoubleProperty(this, "transition", 0.0) {
                @Override
                protected void invalidated() {
                    container.requestLayout();
                    updateExpandCollapseIconRotation();
                }
            };
        }
        return transition;
    }

    private void updateExpandCollapseIconRotation() {
        expandCollapseIcon.setRotate(180 * getTransition());
    }

    private void updateHeader() {
        if (getSkinnable().isCollapsible() && !sectionToolBar.getChildren().contains(expandCollapseButton)) {
            sectionToolBar.getChildren().add(sectionToolBar.getChildren().size(), expandCollapseButton);
        } else if (sectionToolBar.getChildren().contains(expandCollapseButton)) {
            sectionToolBar.getChildren().remove(expandCollapseButton);
        }
    }

}

最后但并非最不重要的行为:

import static javafx.scene.input.KeyCode.SPACE;

import java.util.ArrayList;
import java.util.List;

import com.sun.javafx.scene.control.behavior.BehaviorBase;
import com.sun.javafx.scene.control.behavior.KeyBinding;

import javafx.scene.input.MouseEvent;

@SuppressWarnings("restriction")
public class ContentSectionBehavior extends BehaviorBase<ContentSection> {

    private final ContentSection contentSection;

    public ContentSectionBehavior(final ContentSection contentSection) {
        super(contentSection, CONTENT_SECTION_BINDINGS);
        this.contentSection = contentSection;
    }

    /***************************************************************************
     *                                                                         *
     * Key event handling                                                      *
     *                                                                         *
     **************************************************************************/

    private static final String PRESS_ACTION = "Press";

    protected static final List<KeyBinding> CONTENT_SECTION_BINDINGS = new ArrayList<KeyBinding>();
    static {
        CONTENT_SECTION_BINDINGS.add(new KeyBinding(SPACE, PRESS_ACTION));
    }

    @Override
    protected void callAction(final String name) {
        switch (name) {
            case PRESS_ACTION:
                if (contentSection.isCollapsible() && contentSection.isFocused()) {
                    contentSection.setExpanded(!contentSection.isExpanded());
                    contentSection.requestFocus();
                }
                break;
            default:
                super.callAction(name);
        }
    }

    /***************************************************************************
     *                                                                         *
     * Mouse event handling                                                    *
     *                                                                         *
     **************************************************************************/

    @Override
    public void mousePressed(final MouseEvent e) {
        super.mousePressed(e);
        final ContentSection contentSection = getControl();
        contentSection.requestFocus();
    }

    /**************************************************************************
     *                         State and Functions                            *
     *************************************************************************/

    public void expand() {
        contentSection.setExpanded(true);
    }

    public void collapse() {
        contentSection.setExpanded(false);
    }

    public void toggle() {
        contentSection.setExpanded(!contentSection.isExpanded());
    }

}

为了测试我的实验,我刚刚创建了一个简单的fxml文件:

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

<?import org.test.ContentSection?>
<?import javafx.scene.control.ScrollPane?>
<?import javafx.scene.control.Label?>
<?import javafx.scene.layout.VBox?>
<?import javafx.scene.layout.StackPane?>

<ScrollPane fx:id="root" fitToWidth="true" fitToHeight="true" xmlns="http://javafx.com/javafx/8.0.60" xmlns:fx="http://javafx.com/fxml/1">
    <content>
        <VBox styleClass="content">
            <children>
                <ContentSection fx:id="contentSection" collapsible="false" VBox.vgrow="ALWAYS">
                    <content>
                        <StackPane>
                            <children>
                                <Label text="Test" />
                            </children>
                        </StackPane>
                    </content>
                </ContentSection>
            </children>
        </VBox>
    </content>
</ScrollPane>

现在用SceneBuilder打开这个FXML文件时,唯一发生的事情就是我在SceneBuilder的顶部区域收到一条警告,说“布局失败('null')” .

有人知道接下来该做什么吗?我没有看到搜索问题的选项......

最好的问候Patrick

1 回答

  • 0

    我刚刚遇到同样的错误,问题是 css 样式 . 我不得不删除样式,然后将它们添加到FXML中 .

相关问题