FXMLLoader的使用;将数据添加到由另一个控制器控制的组件中

时间:2018-12-04 12:48:03

标签: java javafx

我正在尝试将数据添加到具有与主应用程序不同的控制器的TableView中。经过一番修补后,我花了一段时间使用静态TableView和用于调用更新的静态方法。但是,我在代码的其他地方遇到了这种方法的问题,some other research使我相信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 :(得分:0)

好吧,DataTable与您先前初始化的MainStage没有关系。 FXML文件中包含的数据表与您之后创建的数据表不同。我建议您在Main.fxml中创建一个Node,然后在加载第二个文件时覆盖此节点。例如,我的代码库之一包含:

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文件中定义的内容窗格中。之后,这应该很容易工作,您不必定义诸如Static Controllers之类的东西。如果您听不懂,我会尝试给您一个完整的例子:)

答案 1 :(得分:0)

如果使用FXMLLoader加载fxml并通过控制器修改生成的场景,则这对通过另一个FXMLLoader.load调用基于同一fxml创建的任何场景均无效。

您的代码假设是这种情况:

致电loader.load();后,有:

  • 2个InteractionTest实例;其中只有一个用作控制器
  • 基于InfoTable.fxml创建的<fx:include source="InfoTable.fxml" />的1个版本以及用于场景一部分的InfoTableController的一个实例。

稍后,在showMainStage方法中,您将使用InfoTableController创建该场景的另一个版本以及另一个tableLoader.load()实例。您永远不会显示此场景,但这是您为其初始化表的唯一场景。对于fxml的第一个版本,您永远不需要进行初始化。

您的监听器中有一个与表中所选项目类似的问题。

我不建议使用Application类作为控制器。而是创建一个单独的控制器类,并使用它来将数据传递到嵌套fxml之间/之间:

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"));
    ...
}