Доступ к дочерним элементам в пользовательском компоненте F XML Init - PullRequest
0 голосов
/ 07 февраля 2020

Если у меня есть пользовательский компонент JavaFX, подобный этому (например):

public class MenuWidget extends VBox implements Initializable {
    @FXML
    StackPane menus;

    public MenuWidget() {
        FXMLLoader fxmlLoader = new FXMLLoader(getClass().getResource("/resources/MenuWidget.fxml"));
        fxmlLoader.setRoot(this);
        fxmlLoader.setController(this);

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

    @Override
    public void initialize(URL location, ResourceBundle resources) {
        System.out.println(menus.getChildren().size());
    }
}

С этим F XML:

<fx:root type="javafx.scene.layout.VBox" prefWidth="300.0" xmlns:fx="http://javafx.com/fxml/1">
    <StackPane fx:id="menus">
        <padding>
            <Insets top="5" left="5" bottom="5" right="5"></Insets>
        </padding>
    </StackPane>    
</fx:root>

И я использую пользовательский компонент, как этот в другом файле F XML:

<MenuWidget>
    <menus>
        <fx:include source="FirstMenu.fxml" />
        <fx:include source="SecondMenu.fxml" />
    </menus>            
</MenuWidget>

Почему метод Initialize () в MenuWidget выводит 0? По сути, мне нужно получить доступ к дочерним элементам стековой панели при создании MenuWidget, чтобы я мог настроить другие элементы управления меню верхнего уровня (которое я удалил из этого примера). Разве FXMLLoader не должен заполнять контроллер (MenuWidget) всеми его свойствами до вызова метода init?

EDIT: выяснил, что init вызывается до завершения конструктора, поэтому попытался переместить код инициализации в конструктор ( после вызова fmxmlLoader.load () и до сих пор не работает.

1 Ответ

3 голосов
/ 07 февраля 2020

Класс MenuWidget и связанный с ним файл F XML полностью автономны. Вы ничего там не включаете и не добавляете детей в StackPane. Другими словами, это:

public class MenuWidget extends VBox implements Initializable {
    @FXML
    StackPane menus;

    public MenuWidget() {
        FXMLLoader fxmlLoader = new FXMLLoader(getClass().getResource("/resources/MenuWidget.fxml"));
        fxmlLoader.setRoot(this);
        fxmlLoader.setController(this);

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

    @Override
    public void initialize(URL location, ResourceBundle resources) {
        System.out.println(menus.getChildren().size());
    }
}

Загружает это:

<fx:root type="javafx.scene.layout.VBox" prefWidth="300.0" xmlns:fx="http://javafx.com/fxml/1">
    <StackPane fx:id="menus">
        <padding>
            <Insets top="5" left="5" bottom="5" right="5"></Insets>
        </padding>
    </StackPane>    
</fx:root>

И как только это закончится, вызывается метод initialize. Ничто там ничего не добавило к menus, поэтому результат вызова menus.getChildren().size(), конечно, 0.

Где-то еще вы загружаете это:

<MenuWidget>
    <menus>
        <fx:include source="FirstMenu.fxml" />
        <fx:include source="SecondMenu.fxml" />
    </menus>            
</MenuWidget>

Что вызывает MenuWidget для создания экземпляра, который включает вызов метода MenuWidget#initialize, , а затем пытается добавить дочерних элементов к menus. Иными словами, если бы это было допустимо и работало F XML, то потомки были бы добавлены после MenuWidget экземпляр был создан и инициализирован.

Однако * Элемент 1028 * должен вызывать в вашем приложении исключение. Класс MenuWidget не определяет свойство списка только для чтения с именем menus. Если вы хотите использовать <menus> и хотите, чтобы элементы этого списка были добавлены в дочерние элементы панели стека menus, измените класс MenuWidget на:

public class MenuWidget extends VBox implements Initializable {
    @FXML
    StackPane menus;

    public MenuWidget() {
        FXMLLoader fxmlLoader = new FXMLLoader(getClass().getResource("/resources/MenuWidget.fxml"));
        fxmlLoader.setRoot(this);
        fxmlLoader.setController(this);

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

    @Override
    public void initialize(URL location, ResourceBundle resources) {
        System.out.println(menus.getChildren().size());
    }

    // add read-only list property (the "property" is read-only, not 
    // the list itself) named "menus"
    public final ObservableList<Node> getMenus() {
        return menus.getChildren();
    }
}

Но это кажется концептуально неправильным (по крайней мере, мне). Я не совсем уверен, что вы пытаетесь сделать, но, может быть, вам следует fx:include - поместить другие файлы F XML непосредственно в файл MenuWidget F XML, а не то, что вы в настоящее время делает. Таким образом, вы можете ввести контроллеры и / или представления (см. вложенные контроллеры ) в класс MenuWidget. Я также не уверен, что использование fx:root полностью оправдано в этом случае, основываясь на том, что вы нам показали. Наследование от VBox, по-видимому, не добавляет каких-либо преимуществ вашему коду (т. Е. Вы не добавляете никакой функциональности), тем более что вы добавляете к нему только одного потомка (затем добавляете потомков к этому потомку). Возможно, более подходящим будет стандартный файл + контроллер F XML.

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...