Использование FXMLLoader;добавление данных в компонент, управляемый другим контроллером - PullRequest
0 голосов
/ 04 декабря 2018

Я пытаюсь добавить данные в TableView, контроллер которого отличается от основного приложения.После некоторой обработки я сделал это некоторое время со статическим TableView и статическим методом для вызова обновлений.Однако я столкнулся с проблемами с этим подходом в другом месте кода, и некоторые другие исследования привели меня к мысли, что FXMLLoader может помочь.

Но данные, которые следует добавить, не отображаются в таблице.System.out.println("adding info"); отображается в консоли дважды, как и ожидалось, но таблица остается пустой.Он был заполнен при использовании статического подхода.Я предполагаю, что мой FXMLLoader создает экземпляр, отличный от того, который был создан при запуске программы.В чем проблема в приведенном ниже коде, в основном в основном классе, в части showMainStage?

Основной класс:

package test.controller;
import java.io.IOException;
import java.util.ArrayList;
import java.util.List;

import javafx.application.Application;
import javafx.fxml.FXMLLoader;
import javafx.scene.Parent;
import javafx.scene.Scene;
import javafx.scene.layout.BorderPane;
import javafx.stage.Stage;

public class InteractionTest extends Application {

    private Stage mainStage;

    /**
     * Show the main window.
     *
     * @throws IOException
     */
    private void showMainStage() throws IOException {
        final FXMLLoader loader = new FXMLLoader();
        loader.setLocation(getClass().getResource("/fxml/Main.fxml"));
        final Parent root = loader.load();
        final BorderPane bp = (BorderPane) root;
        mainStage = new Stage();
        mainStage.setScene(new Scene(bp));
        mainStage.setTitle("Interaction test");
        mainStage.setMaximized(false);
        mainStage.show();

        final Info i1 = new Info("1");
        i1.setPosition(1);
        i1.setTitle("Info 1");
        final Info i2 = new Info("2");
        i2.setPosition(2);
        i2.setTitle("Info 2");
        final List<Info> infoList = new ArrayList<>();
        infoList.add(i1);
        infoList.add(i2);
        final FXMLLoader tableLoader = new FXMLLoader(getClass().getResource("/fxml/InfoTable.fxml"));
        final Parent parent = tableLoader.load();
        final InfoTableController itc = (InfoTableController) tableLoader.getController();
        itc.updateTable(infoList);
    }

    @Override
    public void start(final Stage initStage) throws Exception {
        try {
            showMainStage();
        } catch (final IOException e) {
            e.printStackTrace();
        }
    }

    /**
     * Main method.
     *
     * @param args
     */
    public static void main(final String[] args) {
        launch();
    }

}

TableController:

package test.controller;

import java.io.IOException;
import java.util.List;

import javafx.fxml.FXML;
import javafx.fxml.FXMLLoader;
import javafx.scene.Parent;
import javafx.scene.control.ScrollPane;
import javafx.scene.control.SelectionMode;
import javafx.scene.control.TableColumn;
import javafx.scene.control.TableColumn.SortType;
import javafx.scene.control.TableView;
import javafx.scene.control.cell.PropertyValueFactory;

/**
 * Holds a {@link TableView} to display the infos for further interaction. Is
 * the controller for InfoTable.fxml.
 *
 */
public class InfoTableController {

    /**
     * A {@link ScrollPane} for the {@link Info} table.
     */
    @FXML
    private ScrollPane infoTablePane;

    /**
     * A {@link TableView} for the {@link Info}s.
     */
    private final TableView<Info> table = new TableView<>();

    /**
     * Build the column headers during initialization.
     */
    @FXML
    public void initialize() {
        final TableColumn<Info, String> positionColumn = new TableColumn<>("#");
        positionColumn.setEditable(false);
        positionColumn.setPrefWidth(15.0);
        positionColumn.setMaxWidth(50.0);
        positionColumn.setSortable(true);
        positionColumn.setSortType(SortType.ASCENDING);
        positionColumn.setCellValueFactory(new PropertyValueFactory<>("position"));
        final TableColumn<Info, String> titleColumn = new TableColumn<>("Title");
        titleColumn.setEditable(true);
        titleColumn.setPrefWidth(200.0);
        titleColumn.setMaxWidth(1000.0);
        titleColumn.setCellValueFactory(new PropertyValueFactory<>("title"));

        table.getColumns().addAll(positionColumn, titleColumn);
        table.setColumnResizePolicy(TableView.CONSTRAINED_RESIZE_POLICY);
        table.getSortOrder().add(positionColumn);
        table.getSelectionModel().setSelectionMode(SelectionMode.SINGLE);
        table.getSelectionModel().setCellSelectionEnabled(true);
        table.getSelectionModel().selectedItemProperty().addListener((observable, oldValue, newValue) -> {
            final String id = newValue.getId();
            final FXMLLoader loader = new FXMLLoader(getClass().getResource("/fxml/InfoDisplay.fxml"));
            try {
                final Parent parent = loader.load();
            } catch (final IOException e1) {
                e1.printStackTrace();
            }
            final InfoDisplayController idc = (InfoDisplayController) loader.getController();
            idc.displayDocument(id);
        });
        infoTablePane.setContent(table);
        infoTablePane.setFitToWidth(true);
        infoTablePane.setFitToHeight(true);
    }

    /**
     * Sorts the {@link #table}.
     */
    public void sortTable() {
        table.sort();
    }

    /**
     * Adds table entries.
     *
     */
    public void updateTable(final List<Info> infoList) {
        table.getItems().clear();
        for (final Info info : infoList) {
            System.out.println("adding info");
            table.getItems().add(info);
        }
    }
}

DisplayController:

package test.controller;

import javafx.fxml.FXML;
import javafx.scene.control.ScrollPane;
import javafx.scene.control.Tab;
import javafx.scene.control.TabPane;
import javafx.scene.control.TextArea;

/**
 * Displays infos for reading. Controller for InfoDisplay.fxml.
 *
 */
public class InfoDisplayController {

    @FXML
    private TabPane infoDisplayTabPane;

    /**
     * Constructor.
     */
    public InfoDisplayController() {

    }

    @FXML
    public void initialize() {
        infoDisplayTabPane.getTabs().add(new Tab("test in class"));
    }

    /**
     * Displays the selected document in tabs.
     *
     * @param id The selected document's id.
     */
    public void displayDocument(final String id) {
        // get the underlying Lucene document with the id; omitted for this example
        System.out.println("attempting to display " + id);
        infoDisplayTabPane.getTabs().clear();
        final TextArea textArea = new TextArea("My info text.");
        final ScrollPane scrollPane = new ScrollPane(textArea);
        final Tab tab = new Tab("info", scrollPane);
        infoDisplayTabPane.getTabs().add(tab);
    }
}

Информационный объект:

package test.controller;

import javafx.beans.property.SimpleIntegerProperty;
import javafx.beans.property.SimpleStringProperty;

public class Info {

    /**
     * The id as pulled from the Lucene index.
     */
    private final String id;

    /**
     * The position.
     */
    private final SimpleIntegerProperty position = new SimpleIntegerProperty(0);

    /**
     * The title.
     */
    private final SimpleStringProperty title = new SimpleStringProperty("Title");

    /**
     * Constructor.
     */
    public Info(final String id) {
        this.id = id;
    }

    public String getId() {
        return id;
    }

    public int getPosition() {
        return position.get();
    }

    public void setPosition(final int position) {
        this.position.set(position);
    }

    public String getTitle() {
        return title.get();
    }

    public void setTitle(final String title) {
        this.title.set(title);
    }
}

Main.fxml:

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

<?import javafx.geometry.Insets?>
<?import javafx.scene.control.*?>
<?import javafx.scene.layout.*?>
<?import javafx.scene.text.Text?>

<BorderPane id="BorderPane"
    fx:controller="test.controller.InteractionTest"
    xmlns:fx="http://javafx.com/fxml">
    <top>
    </top>
    <center>
        <SplitPane dividerPositions="0.5" orientation="HORIZONTAL"
            focusTraversable="true">
            <items>
                <fx:include source="InfoTable.fxml" />
                <fx:include source="InfoDisplay.fxml" />
            </items>
        </SplitPane>
    </center>
    <bottom>
    </bottom>
</BorderPane>

InfoTable.fxml

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

<?import javafx.scene.layout.*?>
<?import javafx.scene.control.*?>

<ScrollPane xmlns:fx="http://javafx.com/fxml/1"
    fx:controller="test.controller.InfoTableController"
    fx:id="infoTablePane">
</ScrollPane>

InfoDisplay.fxml

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

<?import javafx.scene.control.*?>
<?import javafx.scene.layout.*?>

<BorderPane xmlns:fx="http://javafx.com/fxml/1"
    prefHeight="500" prefWidth="500" minHeight="200" minWidth="200"
    fx:controller="test.controller.InfoDisplayController">
    <center>
        <TabPane fx:id="infoDisplayTabPane"><Tab text="test"></Tab></TabPane>
    </center>
</BorderPane>

Ответы [ 2 ]

0 голосов
/ 04 декабря 2018

Если вы загружаете fxml с помощью FXMLLoader и изменяете результирующую сцену через контроллер, это не влияет на любые сцены, созданные на основе того же fxml с другим вызовом FXMLLoader.load.

Ваш код предполагает этооднако в этом случае:

После вызова loader.load(); есть:

  • 2 экземпляра InteractionTest;только одна из них используется в качестве контроллера
  • 1 версии InfoTable.fxml, созданной на основе <fx:include source="InfoTable.fxml" />, и одного экземпляра InfoTableController, используемого для части сцены.

Позже в методе showMainStage вы создаете другую версию этой сцены вместе с другим экземпляром InfoTableController, используя tableLoader.load().Вы никогда не показываете эту сцену, но это единственная сцена, для которой вы инициализируете таблицу.Для первой версии fxml вы никогда не выполняете эту инициализацию.

В вашем слушателе есть проблема, похожая на выбранный элемент в таблице.

Я не рекомендую использовать Application класс в качестве контроллера.Вместо этого создайте отдельный класс контроллера и используйте его для передачи данных в / между вложенными fxmls:

Main.fxml

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

<?import javafx.geometry.Insets?>
<?import javafx.scene.control.*?>
<?import javafx.scene.layout.*?>
<?import javafx.scene.text.Text?>

<BorderPane id="BorderPane"
    fx:controller="test.controller.MainController"
    xmlns:fx="http://javafx.com/fxml">
    <center>
        <SplitPane dividerPositions="0.5" orientation="HORIZONTAL"
            focusTraversable="true">
            <items>
                <fx:include fx:id="table" source="InfoTable.fxml" />
                <fx:include fx:id="display" source="InfoDisplay.fxml" />
            </items>
        </SplitPane>
    </center>
</BorderPane>
public class MainController {

    // fields for injection of nested controllers
    @FXML
    private InfoDisplayController displayController;
    @FXML
    private InfoTableController tableController;

    @FXML
    private void initialize() {
        // connect selected table item with displayed document
        tableController.selectedItemProperty().addListener((o, oldValue, newValue) -> {
            final String id = newValue.getId();
            displayController.displayDocument(id);
        });
    }

    public void updateTable(final List<Info> infoList) {
        tableController.updateTable(infoList);
    }
}
public class InfoTableController {

    ...

    @FXML
    public void initialize() {
        ...
//        table.getSelectionModel().selectedItemProperty().addListener((observable, oldValue, newValue) -> {
//            final String id = newValue.getId();
//            final FXMLLoader loader = new FXMLLoader(getClass().getResource("/fxml/InfoDisplay.fxml"));
//            try {
//                final Parent parent = loader.load();
//            } catch (final IOException e1) {
//                e1.printStackTrace();
//            }
//            final InfoDisplayController idc = (InfoDisplayController) loader.getController();
//            idc.displayDocument(id);
//        });
        ...
    }

    public ObjectProperty<Info> selectedItemProperty() {
        return table.getSelectionModel().selectedItemProperty();
    }
}
private void showMainStage() throws IOException {
    final Info i1 = new Info("1");
    i1.setPosition(1);
    i1.setTitle("Info 1");
    final Info i2 = new Info("2");
    i2.setPosition(2);
    i2.setTitle("Info 2");
    final List<Info> infoList = new ArrayList<>();
    infoList.add(i1);
    infoList.add(i2);

    final FXMLLoader loader = new FXMLLoader();
    loader.setLocation(getClass().getResource("/fxml/Main.fxml"));
    final Parent root = loader.load();
    ((MainController)loader.getController()).updateTable(infoList);
    ...


//    final FXMLLoader tableLoader = new FXMLLoader(getClass().getResource("/fxml/InfoTable.fxml"));
    ...
}
0 голосов
/ 04 декабря 2018

Ну, ваш DataTable не имеет отношения к MainStage, который вы инициализировали ранее.Включенный Datatable в FXML-файл отличается от созданного вами впоследствии.Я бы посоветовал вам создать узел в вашем Main.fxml, и когда вы загружаете второй файл, вы переопределяете этот узел.Например, одна из моих баз кода содержит:

loadingWidget.start();
    Task <Parent> task = new Task<Parent>() {
        @Override
        protected Parent call() throws Exception {
            FXMLLoader loader = new FXMLLoader(getClass().getResource("/translationCreator.fxml"));

            return loader.load();
        }       
    };

    task.setOnFailed(new EventHandler<WorkerStateEvent>() {
        @Override
        public void handle(WorkerStateEvent event) {
            loadingWidget.close();
            task.getException().printStackTrace();
            Notifications.create().title("Something went wrong!").text("Error while loading resource tableWindow.fxml").show(); 
        }       
    });

    task.setOnSucceeded(new EventHandler <WorkerStateEvent>() {
        @Override
        public void handle(WorkerStateEvent event) {
            loadingWidget.close();
            contentPane.getChildren().clear();
            contentPane.getChildren().add(task.getValue()); 
        }           
    });

    new Thread(task).start();

Так что я просто добавил свой новый узел в панель содержимого, который я определил в своем файле FXML.После этого это должно сработать, вам не нужно определять что-то вроде статических контроллеров.Если вы не можете этого понять, я постараюсь привести вам полный пример:)

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