【问题标题】:FXMLLoader use; adding data to a component controlled by another controllerFXMLLoader 使用;将数据添加到由另一个控制器控制的组件
【发布时间】:2023-04-07 20:28:01
【问题描述】:

我正在尝试将数据添加到具有与主应用程序不同的控制器的TableView。经过一番修改,我用静态TableView 和调用更新的静态方法做了一段时间。但是,我在代码的其他地方遇到了这种方法的问题,someotherresearch 让我相信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();
    }

}

表控制器:

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

显示控制器:

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>

【问题讨论】:

    标签: java javafx


    【解决方案1】:

    如果您使用 FXMLLoader 加载 fxml 并通过控制器修改生成的场景,这对基于相同 fxml 并通过另一个 FXMLLoader.load 调用创建的任何场景没有影响。

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

    调用loader.load();后有:

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

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

    您的侦听器中存在与表中所选项目类似的问题。

    我不推荐使用Application 类作为控制器。而是创建一个单独的控制器类并使用它将数据传递给嵌套的 fxmls 或在嵌套的 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"));
        ...
    }
    

    【讨论】:

    • 非常感谢,这已经为我解决了问题。最初,我一直想知道 MainController 中的两个 @FXML 字段是如何分配的。但事实证明我在 Main.fxml 中缺少 fx:id="table" 和 fx:id="display" 属性。不知何故,变量名称中的“控制器”位,例如tableController,似乎是自动提供的。这是正确的,还是我仍然错过了什么?
    • @ramundsen 您总是将Controller 附加到fx:id 以获取字段名称,另请参见stackoverflow.com/questions/44467982/…
    【解决方案2】:

    您的 DataTable 与您之前初始化的 MainStage 无关。 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 文件中定义。之后这应该很容易工作,您不必定义静态控制器之类的东西。如果你不能理解这一点,我会试着给你一个完整的例子:)

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 2014-02-04
      • 2021-03-03
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2021-06-11
      • 2013-03-25
      • 1970-01-01
      相关资源
      最近更新 更多