类似于 xaml 的 JavaFX8 列表绑定

JavaFX8 list bindings similar to xaml

我无法在 fxml 中将集合绑定到自定义模板。这是我在 xaml:

中的代码
<ListView ItemsSource="{Binding PersonCollection}">
    <StackPanel Orientation="Horizontal">
        <Label Content="{Binding FirstName}"></Label>
        <ListView ItemsSource="{Binding MiddleNames}">
            <Label Content="{Binding}"></Label>
        </ListView>
        <Label Content="{Binding LastName}"></Label>
    </StackPanel>
</ListView>

这里是模型:

class Person
{
    String FirstName, LastName;
    String[] MiddleNames;
}

布局看起来类似于:

John Ivy
Robert Downey Junior
Max more middlenames in fact even thousands are possible lastname

是否可以将可观察集合绑定到自定义模板? 我尝试了 cellfactory,但无法理解它,因为每个人都只使用字符串。

我不是 100% 确定这是您问题的答案,因为我完全不熟悉 xaml,但希望它是...

示例实现

示例通过将模型对象(Person)设置到 FXML 加载器的命名空间中来工作,这允许您使用 FXML 中的绑定表达式绑定到对象的属性。

在示例中,模型中最初有几个名称,您可以使用添加和删除按钮修改模型中的绑定列表,以在列表中添加或删除更多固定名称。

所有代码都放在名为 sample.names 的包中。

姓名-display.fxml

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

<?import javafx.scene.control.Label?>
<?import javafx.scene.control.ListView?>
<?import javafx.scene.layout.HBox?>

<?import javafx.geometry.Insets?>

<?import javafx.scene.layout.VBox?>
<?import javafx.scene.control.Button?>
<VBox xmlns="http://javafx.com/javafx/8" xmlns:fx="http://javafx.com/fxml/1" fx:controller="sample.names.NameDisplayController">
  <HBox prefHeight="150.0" prefWidth="220.0" spacing="5.0"  >
    <children>
      <Label fx:id="firstNameLabel" text="${person.firstName}" />
      <ListView fx:id="middleNameList" prefWidth="100.0" items = "${person.middleNames}" />
      <Label fx:id="lastNameLabel" text="${person.lastName}" />
    </children>
    <padding>
      <Insets bottom="10.0" left="10.0" right="10.0" top="10.0" />
    </padding>
  </HBox>
  <HBox alignment="CENTER" spacing="30.0">
    <children>
      <Button mnemonicParsing="false" onAction="#addName" text="Add" />
      <Button mnemonicParsing="false" onAction="#removeName" text="Remove" />
    </children>
    <padding>
      <Insets bottom="10.0" left="10.0" right="10.0" top="10.0" />
    </padding>
  </HBox>
</VBox>

NameDisplayApp.java

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

import java.io.IOException;

public class NameDisplayApp extends Application {

    @Override
    public void start(Stage stage) throws IOException {
        Person person = new Person(
                "Bruce",
                new String[] { "Simon", "Larry" },
                "Banner"
        );

        FXMLLoader loader = new FXMLLoader(
                getClass().getResource(
                        "name-display.fxml"
                )
        );
        loader.getNamespace().put(
                "person",
                person
        );
        Pane pane = loader.load();

        NameDisplayController controller = loader.getController();
        controller.setPerson(person);

        stage.setScene(new Scene(pane));
        stage.show();
    }

    public static void main(String[] args) {
        launch(args);
    }
}

NameDisplayController.java

import javafx.collections.FXCollections;
import javafx.collections.ObservableList;
import javafx.event.ActionEvent;

public class NameDisplayController {
    private Person person;

    private ObservableList<String> sampleNames = FXCollections.observableArrayList(
            "George", "Henry", "Wallace"
    );

    public void setPerson(Person person) {
        this.person = person;
    }

    public void addName(ActionEvent actionEvent) {
        if (!sampleNames.isEmpty()) {
            person.getMiddleNames().add(
                sampleNames.remove(0)
            );
        }
    }

    public void removeName(ActionEvent actionEvent) {
        if (!person.getMiddleNames().isEmpty()) {
            sampleNames.add(
                    person.getMiddleNames().remove(0)
            );
        }
    }
}

Person.java

import javafx.beans.property.SimpleStringProperty;
import javafx.beans.property.StringProperty;
import javafx.collections.FXCollections;
import javafx.collections.ObservableList;

public class Person {
    public StringProperty firstName;
    public StringProperty lastName;

    private ObservableList<String> middleNames;

    public Person(String firstName, String[] middleNames, String lastName) {
        this.firstName   = new SimpleStringProperty(firstName);
        this.middleNames = FXCollections.observableArrayList(middleNames);
        this.lastName    = new SimpleStringProperty(lastName);
    }

    public String getFirstName() {
        return firstName.get();
    }

    public StringProperty firstNameProperty() {
        return firstName;
    }

    public void setFirstName(String firstName) {
        this.firstName.set(firstName);
    }

    public ObservableList<String> getMiddleNames() {
        return middleNames;
    }

    public String getLastName() {
        return lastName.get();
    }

    public StringProperty lastNameProperty() {
        return lastName;
    }

    public void setLastName(String lastName) {
        this.lastName.set(lastName);
    }
}

替代实现

可能还有其他(也许更优选的方式)可以做到这一点 - 例如通过关联代码中项目的绑定而不是 FXML(这是我通常做的)或使用依赖注入系统注入模型。有关注入方法的示例,请参阅 afterburner.fx - 虽然我不知道加力器是否也将模型对象放入 FXML 命名空间或仅注入控制器(如果它不注入到 FXML 命名空间)可能是一个很酷的补充,你可以要求它)。