JavaFX 8 TableView 未从 ObservableList 填充(无 FXML)

JavaFX 8 TableView not populating from ObservableList (no FXML)

我相信我已经搜索了所有类似的问题,但仍然没有在任何地方看到我的问题。我正在从根据我的控制台输出成功的数据库中填充 ObservableList。我有一个学校项目的多控制器设置来创建一个调度程序应用程序。我有一个用作 borderPane 的根控制器,两个用于约会和客户的功能控制器,它们都是单例,以及一个不允许每个视图实例化其自己的 DataView 的共享 DataView 控制器。我已经在每个控制器上实现了 toString 以吐出每个元素的值 and/or 对象 ID 是什么,并且一切似乎都对齐了。我一辈子都弄不明白为什么 ListViewTableView 不输出绑定数据。这是我试图绑定到 ListViewTableView

的视图数据
public class AppointmentView implements IAppointmentView {
    private final ZonedDateTime createdDate;
    private final String createdBy;
    // Interface needs these components
    private ReadOnlyStringProperty title;
    private ReadOnlyStringProperty description;
    private ReadOnlyStringProperty location;
    private ReadOnlyStringProperty contact;
    private ReadOnlyStringProperty url;
    private ReadOnlyStringProperty customerName;
    private ReadOnlyObjectProperty<ZonedDateTime> start;
    private ReadOnlyObjectProperty<ZonedDateTime> end;
    private ReadOnlyProperty<ZonedDateTime> lastUpdated;

    /***
     *
     * @param title
     * @param description
     * @param location
     * @param contact
     * @param url
     * @param customerName
     * @param start
     * @param end
     * @param createDate
     * @param createdBy
     * @param lastUpdate
     */
    public AppointmentView(String title, String description, String location, String contact, String url, String customerName, Timestamp start, Timestamp end, Timestamp createDate, String createdBy, Timestamp lastUpdate) {
        this.title = new SimpleStringProperty(title);
        this.description = new SimpleStringProperty(description);
        this.location = new SimpleStringProperty(location);
        this.contact = new SimpleStringProperty(contact);
        this.customerName = new SimpleStringProperty(customerName);
        this.start = new SimpleObjectProperty<>(ZonedDateTime.ofInstant(start.toInstant(), ZoneId.systemDefault()));
        this.end = new SimpleObjectProperty<>(ZonedDateTime.ofInstant(end.toInstant(), ZoneId.systemDefault()));
        this.lastUpdated = new SimpleObjectProperty<>(ZonedDateTime.ofInstant(lastUpdate.toInstant(), ZoneId.systemDefault()));
        this.url = new SimpleStringProperty(url);
        this.createdDate = ZonedDateTime.ofInstant(createDate.toInstant(), ZoneId.systemDefault());
        this.createdBy = createdBy;

    }

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

    ReadOnlyStringProperty titleProperty() {
        return title;
    }

    public String getDescription() {
        return description.getValue();
    }

    ReadOnlyStringProperty descriptionProperty() {
        return description;
    }

    public String getLocation() {
        return location.getValue();
    }

    ReadOnlyStringProperty locationProperty() {
        return location;
    }

    public String getContact() {
        return contact.getValue();
    }

    ReadOnlyStringProperty contactProperty() {
        return contact;
    }

    public String getUrl() {
        return url.getValueSafe();
    }

    ReadOnlyStringProperty urlProperty() {
        return url;
    }

    public String getCustomerName() {
        return customerName.getValue();
    }

    ReadOnlyStringProperty customerNameProperty() {
        return customerName;
    }

    public ZonedDateTime getStart() {
        return ZonedDateTime.ofInstant(start.getValue().toInstant(), ZoneId.systemDefault());
    }

    ReadOnlyProperty<ZonedDateTime> startProperty() {
        return start;
    }

    public ZonedDateTime getEnd() {
        return ZonedDateTime.ofInstant(end.getValue().toInstant(), ZoneId.systemDefault());
    }

    ReadOnlyProperty<ZonedDateTime> endProperty() {
        return end;
    }

    public LocalDate getCreateDate() {
        return createdDate.toLocalDate();
    }

    public String getCreatedBy() {
        return createdBy;
    }

    public ZonedDateTime getLastUpdate() {
        return ZonedDateTime.ofInstant(lastUpdated.getValue().toInstant(), ZoneId.systemDefault());
    }

    ReadOnlyProperty<ZonedDateTime> lastUpdatedProperty() {
        return lastUpdated;
    }

    @Override
    public String toString() {
        final StringBuffer sb = new StringBuffer("AppointmentView{");
        sb.append("createdDate=").append(createdDate);
        sb.append(", createdBy='").append(createdBy).append('\'');
        sb.append(", titleProperty=").append(title);
        sb.append(", descriptionProperty=").append(description);
        sb.append(", locationProperty=").append(location);
        sb.append(", contactProperty=").append(contact);
        sb.append(", urlProperty=").append(url);
        sb.append(", customerName=").append(customerName);
        sb.append(", startProperty=").append(start);
        sb.append(", endProperty=").append(end);
        sb.append(", lastUpdated=").append(lastUpdated);
        sb.append('}');
        return sb.toString();
    }
}

这是 AppViewController:

public class AppViewController extends BorderPane {


    private BorderPane rootPane;
    private MenuBar menuBar;
    private Menu fileMenu;
    private Menu editMenu;
    private Menu reportMenu;
    private Menu helpMenu;
    private MenuItem closeMenuItem;
    private MenuItem copyMenuItem;
    private MenuItem monthlyAppointmentReportMenuItem;
    private MenuItem consultantScheduleMenuItem;
    private MenuItem customersByCountryMenuItem;
    private MenuItem aboutMenuItem;
    private VBox vbAppView;
    private TabPane tpAppPane;
    private Tab tabCustomers;
    private ScrollPane spCustomerEditor;
    private Tab tabAppointments;
    private ScrollPane spAppointmentEditor;
    private MainApp mainApp;
    private static AppViewController instance;
    private static AppointmentViewController appointmentViewController = AppointmentViewController.getInstance();
    private static CustomerViewController customerViewController = CustomerViewController.getInstance();
    private static DataViewController dataViewController;

    private AppViewController() {
        initialize();
    }

    public static AppViewController getInstance() {
        if (instance == null) {
            new AppViewController();
        }
        return instance;
    }

    /**
     * Called to initialize a controller after its root element has been
     * completely processed.
     **/

    public void initialize() {
        instance = this;
        this.rootPane = new BorderPane();
        this.menuBar = new MenuBar();
        this.fileMenu = new Menu("_File");
        this.editMenu = new Menu("_Edit");
        this.reportMenu = new Menu("_Report");
        this.helpMenu = new Menu("_Help");
        this.closeMenuItem = new MenuItem("Close");
        this.copyMenuItem = new MenuItem("Copy");
        this.monthlyAppointmentReportMenuItem = new MenuItem("Monthly Appointment Report");
        this.consultantScheduleMenuItem = new MenuItem("Consultant Schedule Report");
        this.customersByCountryMenuItem = new MenuItem("Customers by Country Report");
        this.aboutMenuItem = new MenuItem("About");
        this.vbAppView = new VBox();
        this.tpAppPane = new TabPane();
        this.tabCustomers = new Tab("Customers");
        this.tabCustomers.setClosable(false);
        this.spCustomerEditor = new ScrollPane();
        this.tabAppointments = new Tab("Appointments");
        this.tabAppointments.setClosable(false);
        this.spAppointmentEditor = new ScrollPane();

        // populate menus and menuBar and add them to top pane

        this.fileMenu.getItems().setAll(closeMenuItem);
        this.fileMenu.setMnemonicParsing(true);
        this.editMenu.getItems().setAll(copyMenuItem);
        this.editMenu.setMnemonicParsing(true);
        this.reportMenu
                .getItems()
                .setAll(monthlyAppointmentReportMenuItem, consultantScheduleMenuItem, customersByCountryMenuItem);
        this.reportMenu.setMnemonicParsing(true);
        this.helpMenu.getItems().setAll(aboutMenuItem);
        this.helpMenu.setMnemonicParsing(true);

        this.menuBar.getMenus().addAll(fileMenu, editMenu, reportMenu, helpMenu);
        this.rootPane.setTop(menuBar);

        // populate scroll panes with included views
        this.spAppointmentEditor.setContent(getAppointmentView());
        this.spCustomerEditor.setContent(getCustomerView());

        // populate tab panes and controllers and add them to the center pane
        this.tabAppointments.setContent(spAppointmentEditor);
        this.tabCustomers.setContent(spCustomerEditor);
        this.tpAppPane.getTabs().addAll(tabAppointments, tabCustomers);

        vbAppView.getChildren().addAll(tpAppPane);
        this.rootPane.setCenter(vbAppView);

        // add data view to bottom pane
        this.rootPane.setBottom(AppointmentViewController.getDataView());

        setupEventHandlers(this);
    }

    private void setupEventHandlers(AppViewController appViewController) {

        this.tabCustomers.setOnSelectionChanged((event -> {
            if (tabCustomers.isSelected()) {
                // Change to Customer View
                setCustomerView();
            } else {
                setAppointmentView();
            }
        }));

        this.tabAppointments.setOnSelectionChanged(event -> {
            if (tabAppointments.isSelected()) {
                setAppointmentView();
            } else {
                setCustomerView();
            }
        });

        this.closeMenuItem.setOnAction(event -> quitApp());

        this.tpAppPane.getSelectionModel().selectedItemProperty().addListener((observable, oldValue, newValue) -> {
            if(newValue.getText().toLowerCase().equals("Customer".toLowerCase())){
                setCustomerView();
            }

            if (newValue.getText().toLowerCase().equals("Appointments".toLowerCase())){
                setAppointmentView();
            }
        });
    }

    private void setCustomerView() {
        CustomerViewController customerViewController = CustomerViewController.getInstance();
        setDataView(customerViewController.getDataViewController().tabPane);
        customerViewController.getGpCustomerEditor().getChildren().filtered(node -> toggleTextFields(node, true));

    }

    private void setAppointmentView() {
        AppointmentViewController controller = AppointmentViewController.getInstance();
        setDataView(controller.getDataViewController().tabPane);
        controller.getGpAppointmentEditor().getChildren().filtered(node -> toggleTextFields(node, true));
    }

    /***
     * Sets whether text fields are enabled or disabled
     * true = disable Text Fields
     * false = enable Text Fields
     * @param node the child nodes of the editor
     * @param disabled whether to disable or enable the text fields
     * @return whether node was affected or not
     */
    public static boolean toggleTextFields(Node node, boolean disabled) {
        if (node instanceof TextField) {
            ((TextField) node).setEditable(!disabled);
            node.setDisable(disabled);
            return true;
        }
        return false;
    }

    public void setMainApp(MainApp mainApp) {
        this.mainApp = mainApp;
    }

    private void quitApp() {
        Platform.exit();
    }

    public Parent getBorderPane() {
        return rootPane;
    }

    public TabPane getTpAppPane() {
        return tpAppPane;
    }

    public Tab getTabCustomers() {
        return tabCustomers;
    }

    public Tab getTabAppointments() {
        return tabAppointments;
    }

    @Override
    public String toString() {
        final StringBuffer sb = new StringBuffer("AppViewController{");
        sb.append("\nrootPane=").append(rootPane);
        sb.append(",\n menuBar=").append(menuBar);
        sb.append(",\n fileMenu=").append(fileMenu);
        sb.append(",\n editMenu=").append(editMenu);
        sb.append(",\n reportMenu=").append(reportMenu);
        sb.append(",\n helpMenu=").append(helpMenu);
        sb.append(",\n closeMenuItem=").append(closeMenuItem);
        sb.append(",\n copyMenuItem=").append(copyMenuItem);
        sb.append(",\n monthlyAppointmentReportMenuItem=").append(monthlyAppointmentReportMenuItem);
        sb.append(",\n consultantScheduleMenuItem=").append(consultantScheduleMenuItem);
        sb.append(",\n customersByCountryMenuItem=").append(customersByCountryMenuItem);
        sb.append(",\n aboutMenuItem=").append(aboutMenuItem);
        sb.append(",\n vbAppView=").append(vbAppView);
        sb.append(",\n tpAppPane=").append(tpAppPane);
        sb.append(",\n tabCustomers=").append(tabCustomers);
        sb.append(",\n spCustomerEditor=").append(spCustomerEditor);
        sb.append(",\n tabAppointments=").append(tabAppointments);
        sb.append(",\n spAppointmentEditor=").append(spAppointmentEditor);
        sb.append(",\n dataViewController=").append(dataViewController);
        sb.append("\n}");
        return sb.toString();
    }

    public void setDataViewController(DataViewController dataViewController) {
        this.dataViewController = dataViewController;
    }

    public DataViewController getDataViewController() {
        return dataViewController;
    }
}

以及约会视图:

private TableView<AppointmentView> tvAppointments = new TableView<>();
private TableColumn<AppointmentView, String> tcTitle = new TableColumn<>("Title");
private TableColumn<AppointmentView, String> tcDescription = new TableColumn<>("Description");
private TableColumn<AppointmentView, String> tcLocation = new TableColumn<>("Location");
private TableColumn<AppointmentView, String> tcContact = new TableColumn<>("Contact");
private TableColumn<AppointmentView, String> tcUrl = new TableColumn<>("URL");
private TableColumn<AppointmentView, String> tcCustomerName = new TableColumn<>("Customer Name");
private TableColumn<AppointmentView, ZonedDateTime> tcStart = new TableColumn<>("Start Time");
private TableColumn<AppointmentView, ZonedDateTime> tcEnd = new TableColumn<>("End Time");
private TableColumn<AppointmentView, ZonedDateTime> tcCreateDate = new TableColumn<>("Created Date");
private TableColumn<AppointmentView, String> tcCreatedBy = new TableColumn<>("Created By");
private TableColumn<AppointmentView, Timestamp> tcLastUpdate = new TableColumn<>("Last Updated");
---- snip ----
    this.tcTitle.setCellValueFactory(new PropertyValueFactory<>("title"));
    this.tcTitle.setVisible(true);
    this.tcTitle.setMinWidth(50);

    this.tcCustomerName.setCellValueFactory(new PropertyValueFactory<>("customerName"));
    this.tcCustomerName.setVisible(true);
    this.tcCustomerName.setMinWidth(40);

    this.tcDescription.setCellValueFactory(new PropertyValueFactory<>("description"));
    this.tcDescription.setVisible(true);
    this.tcDescription.setMinWidth(100);

    this.tcLocation.setCellValueFactory(new PropertyValueFactory<>("location"));
    this.tcLocation.setVisible(true);
    this.tcLocation.setMinWidth(40);

    this.tcContact.setCellValueFactory(new PropertyValueFactory<>("contact"));
    this.tcContact.setVisible(true);
    this.tcContact.setMinWidth(40);

    this.tcUrl.setCellValueFactory(new PropertyValueFactory<>("url".toUpperCase()));
    this.tcUrl.setVisible(true);
    this.tcUrl.setMinWidth(40);

    this.tcStart.setCellValueFactory(new PropertyValueFactory<>("start"));
    this.tcStart.setVisible(true);
    this.tcStart.setMinWidth(40);

    this.tcEnd.setCellValueFactory(new PropertyValueFactory<>("end"));
    this.tcEnd.setVisible(true);
    this.tcEnd.setMinWidth(40);

    this.tcCreateDate.setCellValueFactory(new PropertyValueFactory<>("createDate"));
    this.tcCreateDate.setVisible(true);
    this.tcCreateDate.setMinWidth(40);

    this.tcCreatedBy.setCellValueFactory(new PropertyValueFactory<>("createdBy"));
    this.tcCreatedBy.setVisible(true);
    this.tcCreatedBy.setMinWidth(40);

    this.tcLastUpdate.setCellValueFactory(new PropertyValueFactory<>("lastUpdate"));
    this.tcLastUpdate.setVisible(true);
    this.tcLastUpdate.setMinWidth(40);

    this.tvAppointments = new TableView<>();
    this.tvAppointments.setItems(appointmentViews);
    this.tvAppointments.getColumns().addAll(
            tcTitle,
            tcDescription,
            tcLocation,
            tcContact,
            tcUrl,
            tcCustomerName,
            tcStart,
            tcEnd,
            tcCreateDate,
            tcCreatedBy,
            tcLastUpdate);


    this.lvListView = new ListView<>();
    this.lvListView.setItems(appointmentViews);

    this.dataViewController.setTableView(this.tvAppointments);
    this.dataViewController.setLblListView(new Label("Appointment List"));
    this.dataViewController.setListView(this.lvListView);
    this.dataViewController.setLblTableView(new Label("Appointments"));

以及我正在尝试操作的基础 DataViewController:

public class DataViewController extends TabPane {

//    private static DataViewController instance;
    public TabPane tabPane;
    private Tab tabTableView;
    private Tab tabListView;
    private VBox vbListView;
    private VBox vbTableView;
    private Label lblListView;
    private Label lblTableView;
    protected ScrollPane spListView;
    protected ScrollPane spTableView;
    private ListView<?> listView;
    private TableView<?> tableView;
    private MainApp mainApp;

    public DataViewController() {
        initialize();
    }

/*    public static DataViewController getInstance(){
        if(instance == null){
            new DataViewController();
        }
        return instance;
    }*/

    /**
     * Called to initialize a controller after its root element has been
     * completely processed.
     *
     */
    public void initialize() {
//        this.instance = this;
        this.tabPane = new TabPane();
        this.tabPane.setPrefHeight(250.0);
        this.tabPane.setMaxHeight(400.0);
        this.tabPane.setMaxWidth(Integer.MAX_VALUE);
        this.tabTableView = new Tab("Table View");
        this.tabTableView.setClosable(false);
        this.tabListView = new Tab("List View");
        this.tabListView.setClosable(false);
        this.spListView = new ScrollPane();
        this.spTableView = new ScrollPane();
        this.lblListView = new Label("List View");
        this.lblTableView = new Label("Table View");
        this.vbListView = new VBox();
        this.vbTableView = new VBox();
        this.listView = new ListView<>();
        this.tableView = new TableView<>();

        this.vbListView.getChildren().setAll(this.lblListView, this.listView);
        this.spListView.setContent(this.listView);
        this.tabListView.setContent(this.spListView);


        this.vbTableView.getChildren().setAll(this.lblTableView, this.spTableView);
        this.spTableView.setContent(this.tableView);
        this.tabTableView.setContent(vbTableView);

        this.tabPane.getTabs().setAll(tabListView, tabTableView);
    }

    public Label getLblListView() {
        return lblListView;
    }

    public void setLblListView(Label lblListView) {
        this.lblListView = lblListView;
    }

    public Label getLblTableView() {
        return lblTableView;
    }

    public void setLblTableView(Label lblTableView) {
        this.lblTableView = lblTableView;
    }

    public ListView<?> getListView() {
        return listView;
    }

    public void setListView(ListView<?> listView) {
        this.listView = listView;
    }

    public TableView<?> getTableView() {
        return tableView;
    }

    public void setTableView(TableView<?> tableView) {
        this.tableView = tableView;
    }

    public void setMainApp(MainApp mainApp){
        this.mainApp = mainApp;
    }

/*    public static DataViewController getInstance() {
        if (instance == null){
            instance = new DataViewController();
        }
        return instance;
    }*/

    @Override
    public String toString() {
        return new StringBuilder()
                .append("DataViewController{")
                .append("\ntabPane=")
                .append(tabPane)
                .append(", \ntabTableView=")
                .append(tabTableView)
                .append(", \ntabListView=")
                .append(tabListView)
                .append(", \nvbListView=")
                .append(vbListView)
                .append(", \nvbTableView=")
                .append(vbTableView)
                .append(", \nlblListView=")
                .append(lblListView.getText())
                .append(", \nlblTableView=")
                .append(lblTableView.getText())
                .append(", \nspListView=")
                .append(spListView)
                .append(", \nspTableView=")
                .append(spTableView)
                .append(", \nlistView=")
                .append(listView.getItems())
                .append(", \ntableView=")
                .append(tableView.getColumns())
                .append("\n}")
                .toString();
    }
}

如前所述,我进行了一些 toString 调用和实现,并且我一直在 DataViewController 中看到对象:

listView=[AppointmentView{createdDate=2017-09-02T00:00-07:00[America/Los_Angeles], createdBy='test1', titleProperty=StringProperty [value: Meet with Amari], descriptionProperty=StringProperty [value: Meeting WR of the Raiders], locationProperty=StringProperty [value: Raiders HQ, Alameda], contactProperty=StringProperty [value: Jack DelRio], urlProperty=StringProperty [value: raiders.com], customerName=StringProperty [value: Amari Cooper], startProperty=ObjectProperty [value: 2017-09-04T16:00-07:00[America/Los_Angeles]], endProperty=ObjectProperty [value: 2017-09-04T16:15-07:00[America/Los_Angeles]], lastUpdated=ObjectProperty [value: 2017-09-02T23:07-07:00[America/Los_Angeles]]}, AppointmentView{createdDate=2017-09-02T00:00-07:00[America/Los_Angeles], createdBy='test1', titleProperty=StringProperty [value: Meet with Amari], descriptionProperty=StringProperty [value: Meeting WR of the Raiders], locationProperty=StringProperty [value: Raiders HQ, Alameda], contactProperty=StringProperty [value: Jack DelRio], urlProperty=StringProperty [value: raiders.com], customerName=StringProperty [value: Amari Cooper], startProperty=ObjectProperty [value: 2017-09-04T16:00-07:00[America/Los_Angeles]], endProperty=ObjectProperty [value: 2017-09-04T16:15-07:00[America/Los_Angeles]], lastUpdated=ObjectProperty [value: 2017-09-02T23:07-07:00[America/Los_Angeles]]}], 
tableView=[javafx.scene.control.TableColumn@6167e82, javafx.scene.control.TableColumn@50658769, javafx.scene.control.TableColumn@f65aae1, javafx.scene.control.TableColumn@2d5ab3ab, javafx.scene.control.TableColumn@180d2aec, javafx.scene.control.TableColumn@64afb84b, javafx.scene.control.TableColumn@463f349d, javafx.scene.control.TableColumn@3e80101a, javafx.scene.control.TableColumn@4fab076c, javafx.scene.control.TableColumn@565f8332, javafx.scene.control.TableColumn@697bdeb8]

下面是调用它的 MainApp 方法:

private void initLayout() {
//        rootPane = new BorderPane();



    appointmentViewController = AppointmentViewController.getInstance();
    appointmentView = appointmentViewController.apAppointmentView;
    appointmentViewController.setMainApp(this);

    dataViewController = appointmentViewController.getDataViewController();
    dataView = dataViewController.tabPane;
    dataViewController.setMainApp(this);

    customerViewController = CustomerViewController.getInstance();
    customerView = customerViewController.apCustomerView;
    customerViewController.setMainApp(this);

    appViewController = AppViewController.getInstance();
    appView = appViewController.getBorderPane();
    appViewController.setMainApp(this);

    appViewController.setDataViewController(appointmentViewController.getDataViewController());

    System.out.println(this.dataViewController);
    System.out.println(this.dataView);
    System.out.println(appointmentViewController.toString());
    System.out.println(customerViewController.toString());
    System.out.println(appViewController.toString());

    rootPane = (BorderPane) appView;


    scene = new Scene(rootPane);
    scene.getStylesheets().add("/styles/Styles.css");

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

我已经设置了 CellValueFactories 等。我在这里错过了什么?

UI 是这样显示的:

DataViewController中,场景结构是从构造函数初始化的。使用此 class 中的任何 setter 修改字段,它确实 not 以任何方式修改场景,留下旧的空白 ListView 在场景中,但打印包含来自 toString 方法的项目的新场景。