使用 FXML 初始化带有 fx:root 内列的 TableView

Initializing TableView with columns inside fx:root using FXML

我在名为 TablePatientView 的 fxml 文件中有一个自定义 class。我像 <TablePatientView fx:id="tablePatientView" layoutX="20.0" layoutY="45.0" prefHeight="409.0" prefWidth="363.0" /> 一样使用它,它几乎可以正常工作。 fx:root 如下所示:

<?xml version="1.0" encoding="UTF-8"?>
<?import javafx.scene.control.*?>

<fx:root type="javafx.scene.control.TableView" xmlns:fx="http://javafx.com/fxml">
    <TableView>
        <columns>
            <TableColumn fx:id="patId" prefWidth="101.0" text="Patient-ID" />
            <TableColumn fx:id="patVorname" prefWidth="69.0" text="Vorname" />
            <TableColumn fx:id="patNachname" prefWidth="98.0" text="Nachname" />
            <TableColumn fx:id="patGebdat" prefWidth="96.0" text="Geburtsdatum" />
        </columns>
    </TableView>
</fx:root>

与以下 class

public class TablePatientView extends TableView<TablePatient> {

    private Parent root;

    @FXML
    private TableColumn<TablePatient,String> patId;
    @FXML
    private TableColumn<TablePatient,String> patVorname;
    @FXML
    private TableColumn<TablePatient,String> patNachname;
    @FXML
    private TableColumn<TablePatient, LocalDate> patGebdat;

    public TablePatientView() {
        FXMLLoader loader = new FXMLLoader(Main.class.getResource("/fxml/TablePatientView.fxml"));
        loader.setController(this);
        loader.setRoot(this);
        try {
            root = loader.load();
        } catch (IOException e) {
            e.printStackTrace();
        }
        setCellValueFactories();
        // getColumns().addAll(patId, patVorname, patNachname, patGebdat); works, but not with plain fxml
    }

    private void setCellValueFactories() {
        patId.setCellValueFactory(param -> param.getValue().idProperty().asString());
        patVorname.setCellValueFactory(param -> param.getValue().vornameProperty());
        patNachname.setCellValueFactory(param -> param.getValue().nachnameProperty());
        patGebdat.setCellValueFactory(param -> param.getValue().gebDatProperty());
    }
}

列已正确初始化,但由于某种原因未作为列添加到 TableView。 我知道我可以通过 java 代码来做到这一点(请参阅上面代码中的注释行)但我想知道为什么这不适用于 fxml。在没有自定义 class 和 root 的情况下直接在 fxml 中使用 就可以了。

<fx:root type="TableView"> 引用 TableView,它是通过调用 loader.setRoot(this).

提供的

您在 TableView1 中定义了另一个 TableView。这些列被添加到 TableView,而不是 <fx:root>.

引用的列

正确的 FXML 是

<?xml version="1.0" encoding="UTF-8"?>
<?import javafx.scene.control.*?>

<fx:root type="javafx.scene.control.TableView" xmlns:fx="http://javafx.com/fxml">
    <columns>
        <TableColumn fx:id="patId" prefWidth="101.0" text="Patient-ID" />
        <TableColumn fx:id="patVorname" prefWidth="69.0" text="Vorname" />
        <TableColumn fx:id="patNachname" prefWidth="98.0" text="Nachname" />
        <TableColumn fx:id="patGebdat" prefWidth="96.0" text="Geburtsdatum" />
    </columns>
</fx:root> 

替代方法

我不太喜欢子类化控件 类,例如 TableView。原因是您并没有真正向 TableView 添加功能,您实际上只是在其上设置属性,这不是对继承的良好使用。

考虑使用创建模式。一个简单的静态工厂方法运行良好,并通过 fx:factory 属性与 FXML 很好地配合使用。

TablePatientView.fxml:

<?xml version="1.0" encoding="UTF-8"?>
<?import javafx.scene.control.*?>

<TableView xmlns:fx="http://javafx.com/fxml" fx:controller="com.jamesd.examples.TablePatientController">
    <columns>
        <TableColumn fx:id="patId" prefWidth="101.0" text="Patient-ID" />
        <TableColumn fx:id="patVorname" prefWidth="69.0" text="Vorname" />
        <TableColumn fx:id="patNachname" prefWidth="98.0" text="Nachname" />
        <TableColumn fx:id="patGebdat" prefWidth="96.0" text="Geburtsdatum" />
    </columns>
</TableView>

TablePatientController.java:

public class TablePatientController {

    @FXML
    private TableColumn<TablePatient,String> patId;
    @FXML
    private TableColumn<TablePatient,String> patVorname;
    @FXML
    private TableColumn<TablePatient,String> patNachname;
    @FXML
    private TableColumn<TablePatient, LocalDate> patGebdat;
    
    @FXML
    private void initialize() {
        setCellValueFactories();
    }

    private void setCellValueFactories() {
        patId.setCellValueFactory(param -> param.getValue().idProperty().asString());
        patVorname.setCellValueFactory(param -> param.getValue().vornameProperty());
        patNachname.setCellValueFactory(param -> param.getValue().nachnameProperty());
        patGebdat.setCellValueFactory(param -> param.getValue().gebDatProperty());
    }
}

Tables.java:

public class Tables {

    public static TableView<TablePatient> tablePatientView() throws Exception {
        FXMLLoader loader = new FXMLLoader(Main.class.getResource("/fxml/TablePatientView.fxml"));
        return loader.load();
    }
}

然后您可以在 FXML 中使用它

<Tables fx:factory="tablePatientView" />

脚注

  1. 这里发生的是第二个 TableView 被添加到原始 TableViewitems 列表中,因为 TableView@DefaultProperty是那个清单。所以最终 table 视图会尝试将其显示为 table 中的一行。似乎在某个时候应该有一个 ClassCastException,但它可能会被静静地压扁,因为 FXML 无法指定泛型类型。