it-swarm.com.ru

Загрузка нового fxml в той же сцене

У меня есть 2 файла FXML:

  • Макет (заголовок, меню и контент) 
  • Anchorpane (он должен быть размещен внутри содержимого из другого файла FXML)

Я хотел бы знать, как я могу загрузить второй файл в пространство содержимого из сцены «Мастер». И это хорошо для работы в javaFX или лучше загрузить новую сцену?

Я пытаюсь сделать что-то вроде этого, но это не работает:

@FXML
private AnchorPane content;

@FXML
private void handleButtonAction(ActionEvent event) {        
    content = (AnchorPane) FXMLLoader.load("Vista2.fxml");
}

Спасибо за помощь.

33
Andre

Почему ваш код не работает

Загрузчик создает новую AnchorPane, но вы никогда не добавите новую панель к родителю в графе сцены.

Быстрая починка

Вместо:

content = (AnchorPane) FXMLLoader.load("Vista2.fxml");

Написать:

content.getChildren().setAll(FXMLLoader.load("Vista2.fxml"));

Замена контента детей с вашей новой Vista. Сам контент остается в графе сцены, поэтому, когда вы устанавливаете его дочерние элементы, вы также одновременно присоединяете их к графе сцены.

Возможно, вам придется поэкспериментировать с макетом (например, работать с автоматическим изменением размера макетов, таких как StackPanes, а не AnchorPanes), чтобы получить точное поведение, которое вы хотите.

Вместо того, чтобы просто принять быстрое исправление, я бы посоветовал рассмотреть простую платформу, связанную ниже, поскольку она может предоставить вам более универсальный механизм для получения желаемого поведения.

Ссылочная структура навигации FXML

Я создал небольшой фреймворк для обмена панелями содержимого, контролируемыми с помощью fxml, в часть главной сцены и из нее. 

Механизм каркаса такой же, как предложено в ответе Китрила. 

  1. Основная панель для внешнего fxml действует как держатель дочерних панелей. 
  2. Основной контроллер для внешнего fxml предоставляет открытый метод, который можно использовать для замены дочерних панелей. 
  3. Удобный класс навигатора статически инициализируется главным контроллером для внешнего макета. 
  4. Навигатор предоставляет открытый статический метод для загрузки новой дочерней панели в основной контейнер (путем вызова метода на главном контроллере).
  5. Дочерние панели генерируются в навигаторе соответствующими загрузчиками fxml. 

Почему Framework

Фреймворк кажется излишним для ответа на ваш вопрос, и, возможно, так оно и есть. Однако я обнаружил, что две наиболее часто задаваемые темы, связанные с FXML:

  1. Навигация между панелями, сгенерированными FXML (этот вопрос).
  2. Как передавать данные между контроллерами FXML .

Поэтому я почувствовал, что для этого случая была необходима небольшая демонстрационная структура.

Пример выходных данных фреймворка

Первый экран показывает макет приложения, отображающий первую Vista. Содержимое представляет собой заголовок, который определен в основном макете приложения, и цветную сменную дочернюю панель aliceblue.

Vista1

На следующем экране пользователь перешел ко второй Vista, которая сохраняет постоянный заголовок из основного макета и заменяет исходную дочернюю панель на новую дочернюю панель кораллового цвета. Новый дочерний элемент был загружен из нового файла fxml.

Vista2

Ищете что-то более существенное?

Облегченный фреймворк, который является более обширным и лучше поддерживается, чем пример фреймворка из этого вопроса, называется afterburner.fx .

Ищете что-то еще проще?

Просто поменяйте местами корень сцены: Изменение сцен в JavaFX .

Другие опции?

Анимированные переходы и другие: Переключение между панелями в JavaFX

64
jewelsea

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

https://www.youtube.com/watch?v=LDVztNtJWOo

Насколько я понял, что здесь происходит, это (это действительно похоже на то, что происходит в методе Start () в классе приложения):

private void buttonGoToWindow3Action(ActionEvent event) throws IOException{
    Parent window3; //we need to load the layout that we want to swap
    window3 = (StackPane)FXMLLoader.load(getClass().getResource("/ScenePackage/FXMLWindow3"));

    Scene newScene; //then we create a new scene with our new layout
    newScene = new Scene(window3);

    Stage mainWindow; //Here is the magic. We get the reference to main Stage.
    mainWindow = (Stage)  ((Node)event.getSource()).getScene().getWindow();

    mainWindow.setScene(newScene); //here we simply set the new scene
}

Однако я не эксперт по Java и довольно новичок в программировании, поэтому было бы хорошо, если бы кто-то опытный оценил его.

Правка: Я нашел еще более простой метод;

Перейдите в класс MainApplication и создайте статическую сцену parentWindow.

public static Stage parentWindow;
@Override
public void start(Stage stage) throws Exception {
    parentWindow = stage;

    Parent root = FXMLLoader.load(getClass().getResource("/ScenePackage/FXMLMainScene.fxml"));

    Scene scene = new Scene(root);

    stage.setScene(scene);
    stage.show();
}

Теперь вы получаете доступ к своей основной сцене, поэтому в любом месте программы вы можете сделать что-то подобное, чтобы изменить сцену:

    Parent window1;
    window1 = FXMLLoader.load(getClass().getResource("/ScenePackage/FXMLWindow1.fxml"));

    //Scene newSceneWindow1 = new Scene(window1);

    Stage mainStage;
    //mainStage = (Stage)  ((Node)event.getSource()).getScene().getWindow();
    mainStage = MainApplication.parentWindow;
    mainStage.getScene().setRoot(newSceneWindow1); //we dont need to change whole sceene, only set new root.
5
Tomasz Mularczyk

Мой пример маска .

С помощью:

Main.getNavigation().load(View2.URL_FXML).Show();
Main.getNavigation().GoBack();
0
vas7n

Если вы ищете способ заставить кнопку вызывать новый файл fxml, это сработало для меня.

@FXML
private void mainBClicked(ActionEvent event) throws IOException {
    Stage stage;
    Parent root;
    stage=(Stage) ((Button)(event.getSource())).getScene().getWindow();
    root = FXMLLoader.load(getClass().getResource("MainMenu.fxml"));
    Scene scene = new Scene(root);
    stage.setScene(scene);
    stage.show();
}
0
Alekya

В этом случае я рекомендую вам использовать пользовательский компонент вместо этого. Сначала создайте пользовательский компонент для вашего контента:

class Content2 extends AnchorPane {
    Content() {
        FXMLLoader loader = new FXMLLoader(getClass().getResource("Vista2.fxml");
        loader.setRoot(this);
        loader.setController(this);
        loader.load();
    }
}

Замените разметку AnchorPane в корне вашего файла Vista2.fxml на fx:root:

<fx:root type="javafx.scene.layout.AnchorPane" xmlns:fx="http://javafx.com/fxml">
    ...
</fx:root>

Затем вы можете сделать это просто с помощью пользовательской привязки события и функции стрелки. Добавьте свойство обработчика событий в свой класс Content:

private final ObjectProperty<EventHandler<ActionEvent>> propertyOnPreviousButtonClick = new SimpleObjectProperty<EventHandler<ActionEvent>>();

@FXML
private void onPreviousButtonClick(ActionEvent event) {
    propertyOnPreviousButtonClick.get().handle(event)
}

public void setOnPreviousButtonClick(EventHandler<ActionEvent> handler) {
    propertyOnPreviousButtonClick.set(handler);
}

Наконец, свяжите ваш собственный обработчик событий с вашим кодом Java или fxml:

@FXML
onNextButtonClick() {
    Content2 content2 = new Content2();
    content2.setOnPreviousButtonClick((event) -> {
        Content1 content1 = new Content1();

        layout.getChildren().clear();
        layout.getChildren().add(content1);
    });

    layout.getChildren().clear();
    layout.getChildren().add(content2);    
}

Если вы не хотите добавлять контент динамически, просто setVisible() to true или false

0
Clite Tailor

Застрял в этом тоже Испытал большинство ответов, не то, что я хотел, поэтому я просто использовал идеалы, данные для этого:

public class Main extends Application {
public static Stage homeStage;
@Override
public void start(Stage primaryStage) throws Exception{
    homeStage = primaryStage;
    Parent root = FXMLLoader.load(getClass().getResource("mainView.fxml"));
    root.getStylesheets().add(getClass().getResource("stylesheet/custom.css").toExternalForm());
    homeStage.setTitle("Classification of Living Organisms");
    homeStage.setScene(new Scene(root, 600, 500));
    homeStage.show();
}


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

это мой основной класс. Main.Java с целевым окном/страницей mainView.fxml . Использовал немного идеи @Tomasz, хотя и смутил меня, прежде чем я сделал это в своем классе mainController.Java:

public void gotoSubMenu(Event event) {
    Parent window1;
    try {
        window1 = FXMLLoader.load(getClass().getResource("src/displayView.fxml"));
        Stage window1Stage;
        Scene window1Scene = new Scene(window1, 600, 500);
        window1Stage = Main.homeStage;
        window1Stage.setScene(window1Scene);
    } catch (IOException e) {
        e.printStackTrace();
    }
}

создал новое родительское окно с именем 'window1', которое загрузило второй fxml-файл с именем 'displayView.fxml' в каталог src . создал объект стадии основного вида и установил сцену во вновь созданную сцену, корнем которой является window1 . Надеюсь, это поможет тем, кто входит в #JavaFX сейчас.

0
ConnelBLAZE

У других может быть лучшее решение, но мое решение состояло в том, чтобы иметь простой контейнер, такой как VBox, во внешнем fxml, затем загрузить новый контент и добавить его как дочерний элемент контейнера. Если вы загружаете только одну или две формы, это может быть путь. Однако, для более полной структуры, я нашел этот пост полезным: https://blogs.Oracle.com/acaicedo/entry/managing_multiple_screens_in_javafx1 У нее есть исходный код для ее структуры, который включает в себя причудливые переходы. Хотя он предназначен для управления сценами верхнего уровня, я обнаружил, что его легко адаптировать и для управления внутренними областями контента.

0
kithril