Пользовательский элемент управления JavaFX — корневое значение, уже указанное в Scene Builder 2.0

Я реализовал пользовательский элемент управления, используя файл fxml и класс Java, подобно тому, как это описано в это официальное руководство (см. код ниже). Обратите внимание, что корневой элемент fxml определен с помощью fx:root, и я программно вызываю setRoot.

Я попытался включить элемент управления в макет приложения FXML, и приложение загружается нормально (и отображает элемент управления, как и ожидалось).

Однако, если я попытаюсь импортировать файл jar, содержащий мой элемент управления, в Scene Builder 2.0, элемент управления не появится в списке компонентов для импорта (в отличие от некоторых других элементов управления в том же банке). Если я выберу «Показать отчет об анализе JAR», он покажет ошибку, вызванную javafx.fxml.LoadException: Root value already specified.

Вы знаете, почему я получаю эту ошибку при загрузке в Scene Builder, хотя в реальном приложении он загружается правильно?

Вот FXML:

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

<?import javafx.scene.control.*?>
<?import javafx.scene.image.ImageView?>
<?import javafx.scene.layout.*?>
<?import javafx.scene.text.Font?>

<fx:root type="javafx.scene.layout.GridPane" id="MediaMetadataDisplay" hgap="20.0" maxHeight="-Infinity" maxWidth="-Infinity" minHeight="-Infinity" minWidth="-Infinity" prefHeight="200.0"
         prefWidth="600.0" xmlns="http://javafx.com/javafx/8" xmlns:fx="http://javafx.com/fxml/1">
    <columnConstraints>
        <ColumnConstraints fillWidth="false" hgrow="NEVER" maxWidth="-Infinity" minWidth="-Infinity" prefWidth="200.0"/>
        <ColumnConstraints halignment="LEFT" hgrow="ALWAYS"/>
    </columnConstraints>
    <rowConstraints>
        <RowConstraints maxHeight="-Infinity" minHeight="-Infinity" prefHeight="30.0" vgrow="SOMETIMES"/>
        <RowConstraints maxHeight="-Infinity" minHeight="-Infinity" prefHeight="40.0" vgrow="SOMETIMES"/>
        <RowConstraints maxHeight="-Infinity" minHeight="-Infinity" prefHeight="25.0" vgrow="SOMETIMES"/>
        <RowConstraints maxHeight="-Infinity" minHeight="-Infinity" prefHeight="25.0" vgrow="SOMETIMES"/>
        <RowConstraints maxHeight="-Infinity" minHeight="-Infinity" prefHeight="25.0" vgrow="SOMETIMES"/>
        <RowConstraints maxHeight="-Infinity" minHeight="-Infinity" prefHeight="25.0" vgrow="SOMETIMES"/>
        <RowConstraints maxHeight="-Infinity" minHeight="-Infinity" prefHeight="30.0" vgrow="SOMETIMES"/>
    </rowConstraints>
    <children>
        <ImageView id="coverView" fx:id="coverView" fitHeight="200.0" fitWidth="200.0" pickOnBounds="true" preserveRatio="true" GridPane.rowSpan="7"/>
        <Label id="trackName" fx:id="trackName" maxWidth="1.7976931348623157E308" text="trackName" GridPane.columnIndex="1" GridPane.rowIndex="1">
            <font>
                <Font name="System Bold" size="16.0"/>
            </font>
        </Label>
        <Label id="artist" fx:id="artist" maxWidth="1.7976931348623157E308" text="artist" GridPane.columnIndex="1" GridPane.rowIndex="2"/>
        <Label id="album" fx:id="album" maxWidth="1.7976931348623157E308" text="album" GridPane.columnIndex="1" GridPane.rowIndex="3"/>
        <Label id="genre" fx:id="genre" maxWidth="1.7976931348623157E308" text="genre" GridPane.columnIndex="1" GridPane.rowIndex="4"/>
        <Label id="trackNumber" fx:id="trackNumber" maxWidth="1.7976931348623157E308" text="trackNumber" GridPane.columnIndex="1" GridPane.rowIndex="5"/>
    </children>
</fx:root>

И элемент контроллера/корня Java:

package customjavafx.scene.control;

import javafx.beans.property.ObjectProperty;
import javafx.beans.property.SimpleObjectProperty;
import javafx.fxml.FXML;
import javafx.fxml.FXMLLoader;
import javafx.scene.control.Label;
import javafx.scene.image.Image;
import javafx.scene.image.ImageView;
import javafx.scene.layout.GridPane;
import javafx.scene.media.Media;

import java.io.IOException;
import java.util.Map;

public class MediaMetadataDisplay extends GridPane {

    public MediaMetadataDisplay() {
        final FXMLLoader fxmlLoader = new FXMLLoader(getClass().getResource("MediaMetadataDisplay.fxml"));
        fxmlLoader.setRoot(this);
        fxmlLoader.setController(this);
        try {
            fxmlLoader.load();
        } catch (IOException exception) {
            throw new RuntimeException(exception);
        }

        media.addListener((obs, oldVal, newVal) -> updateMedia(newVal));
    }

    private final ObjectProperty<Media> media = new SimpleObjectProperty<>((Media) null, "media");
    @FXML private ImageView coverView;
    @FXML private Label trackName;
    @FXML private Label artist;
    @FXML private Label album;
    @FXML private Label genre;
    @FXML private Label trackNumber;

    public void updateMedia(Media media) {
        // TODO show updated metadata
    }

    public ObjectProperty<Media> mediaProperty() {
        return media;
    }
    public Media getMedia() {
        return mediaProperty().get();
    }
    public void setMedia(final Media media) {
        mediaProperty().set(media);
    }
}

Причина ошибки в трассировке стека, показанной Scene Builder:

Caused by: javafx.fxml.LoadException: Root value already specified.
file:/Users/guillaumegaly/Library/Application%20Support/Scene%20Builder/Library/custom-controls_2.11.jar!/customjavafx/scene/control/MediaMetadataDisplay.fxml

    at javafx.fxml.FXMLLoader.constructLoadException(FXMLLoader.java:2613)
    at javafx.fxml.FXMLLoader.createElement(FXMLLoader.java:2771)
    at javafx.fxml.FXMLLoader.processStartElement(FXMLLoader.java:2720)
    at javafx.fxml.FXMLLoader.loadImpl(FXMLLoader.java:2527)
    at javafx.fxml.FXMLLoader.loadImpl(FXMLLoader.java:2441)
    at javafx.fxml.FXMLLoader.load(FXMLLoader.java:2409)
    at customjavafx.scene.control.MediaMetadataDisplay.<init>(MediaMetadataDisplay.java:26)
    ... 18 more

person Cyäegha    schedule 08.09.2014    source источник
comment
При беглом просмотре ваш код выглядит нормально, поэтому я не уверен, почему он не работает (хотя я не пробовал использовать его в SceneBuilder). Возможно, просматривая блог Роба Терпа на импорт пользовательских элементов управления в SceneBuilder может дать некоторую информацию. Кроме того, вы можете опубликовать свой вопрос на форумах Oracle JavaFX; Я считаю, что разработчики SceneBuilder иногда мониторят эти форумы.   -  person jewelsea    schedule 09.09.2014
comment
Я тоже видел этот учебник, мне он не кажется другим, за исключением расширения другого макета (AnchorPane против GridPane). Я разместил свой вопрос на форумах Oracle, как вы предложили, посмотрим, как пойдет.   -  person Cyäegha    schedule 09.09.2014


Ответы (2)


Удалить строку

fxmlLoader.setRoot(this);

Ваш FXML определяет корень как AnchorPane (и вы не можете установить корень дважды, поэтому вы получаете ошибку).

<fx:root type="javafx.scene.layout.GridPane" id="MediaMetadataDisplay" hgap="20.0" maxHeight="-Infinity" maxWidth="-Infinity" minHeight="-Infinity" minWidth="-Infinity" prefHeight="200.0"
         prefWidth="600.0" xmlns="http://javafx.com/javafx/8" xmlns:fx="http://javafx.com/fxml/1">

Я очень благодарен этому посту

Корневое значение уже указано

person swapyonubuntu    schedule 13.08.2015
comment
Если я удалю setRoot, то как он узнает, куда прикреплять компоненты? - person Dims; 02.05.2016
comment
Тег Fx:root установит для вас root. По сути, это javafx. Fxml-файл - person swapyonubuntu; 02.05.2016
comment
У меня как-то возникает обратная проблема (при использовании синтаксиса <fx:root... в файле .fxml), если я не вызываю setRoot(), я получаю ошибку no root specified. - person gbmhunter; 25.08.2016

Я следил за учебными пособиями Javafx FXML по документации и застрял в разделе пользовательских компонентов. Проблема для меня заключалась в том, что когда я вызывал метод setRoot, он выдавал ошибку «Root уже указан». Лично я исправил это, добавив корневой тег в документ FXML пользовательского компонента. Теперь это работает.

Я добавляю это, чтобы, если у кого-то еще возникла такая же проблема, Google мог дать им ответ.

person Dean Irwin    schedule 08.02.2018