将自定义UI元素与数据相关联

时间:2015-10-27 11:56:33

标签: java javafx

我正在考虑如何以一种好的方式做到这一点。 我创建了一个程序,在GUI中显示一些自定义形状。现在当我将鼠标悬停在一个元素上时,我想显示一些与该元素相关的信息。 在这个例子中,让我们想象每个元素都是一个人,所以我想在鼠标悬停时显示一些关于那个人的基本信息(姓名,出生日期,性别......)。

如何将关于某个人的数据连接到元素数据代表该人?

据我所知,有办法做到这一点。

  1. 我将数据直接嵌入到形状中。 (不知道该怎么做)
  2. 我使用形状的ID来查找数据。

2 个答案:

答案 0 :(得分:0)

这是一个不同的解决方案,我认为它比使用基本上无类型的userDataproperties map更强大。使用这些方法中的任何一种都会使代码混乱,并检查null和正确的类型,或者强制您假设整个应用程序的代码都使用正确的UI元素。第一个将使您更难以阅读(并因此维护)您的代码。第二个意味着您的代码容易受到难以追踪的错误的影响(因为它们出现在可能远离其实现的代码的代码中)。

要么通过子类化它们来创建UI元素,要么您有一个方法(或一些代码块)来创建它们。在第一种情况下,您只需为子类提供所需数据的引用:

public class PersonNode extends Region {

    private final Person person ;

    /**
    * @parameter person The person this node represents.
    * @throws NullPointerException if person is null.
    **/
    public PersonNode(Person person) {
        if (person == null) throw new NullPointerException("Person cannot be null");

        // do layout, add nodes to children list, etc...

        this.person = person ;

        Popup popup = createPopup();
        this.setOnMouseEntered(e -> popup.show(this, e.getScreenX(), e.getScreenY()));
        this.setOnMouseExited(e -> popup.hide());

    }

    private Popup createPopup() {
        Label nameLabel = new Label();
        nameLabel.textProperty().bind(person.nameProperty());
        Label birthdayLabel = new Label();
        DateTimeFormatter formatter = DateTimeFormatter.ofPattern("d MMMM yyyy");
        birthdayLabel.textProperty().bind(Bindings.createStringBinding(
            () -> formatter.format(person.getBirthday()),
            person.birthdayProperty()
        ));
        // etc...
        VBox root = new VBox(10, nameLabel, birthdayLabel);
        Popup popup = new Popup();
        popup.getContent().add(root);
        return popup ;
    }

    public Person getPerson() {
        return person ;
    }
}

现在您的应用程序代码很简单:

List<Person> personList = ... ;
Pane somePane = ... ;

somePane.getChildren().addAll(
    personList.stream().map(PersonNode::new).collect(Collectors.toList()));

并且添加其他UI功能也很简单:

for (Person person : personList) {
    PersonNode node = new PersonNode(person);
    node.setOnMouseClicked(e -> showPersonEditor(node.getPerson()));
    somePane.getChildren().add(node);
}

即使您不想要自定义UI类,也可以通过将节点创建分解为方法来获得更多相同的效果:

public Node createNodeForPerson(Person person) {

    Pane personNode = new Pane(); // probably actually use a real layout pane here...
    // configure personNode with controls or graphics, etc

    Label nameLabel = new Label();
    nameLabel.textProperty().bind(person.nameProperty());
    Label birthdayLabel = new Label();
    DateTimeFormatter formatter = DateTimeFormatter.ofPattern("d MMMM yyyy");
    birthdayLabel.textProperty().bind(Bindings.createStringBinding(
        () -> formatter.format(person.getBirthday()),
        person.birthdayProperty()
    ));
    // etc...
    VBox root = new VBox(10, nameLabel, birthdayLabel);
    Popup popup = new Popup();
    popup.getContent().add(root);

    personNode.setOnMouseEntered(e -> popup.show(personNode, e.getScreenX(), e.getScreenY()));
    personNode.setOnMouseExited(e -> popup.hide());
    return personNode ;
}

(这里发生的是弹出窗口包含通过定义的绑定对Person的引用。personNode通过鼠标侦听器保存对弹出窗口的引用,因此它有效地保留了对Person数据的引用。)

当然,你可以做像

这样的事情
for (Person person : personList) {
    somePane.getChildren().add(createNodeForPerson(person));
}

答案 1 :(得分:-1)

您可以使用setUserData(Object)getUserData()

将自定义数据与任何JavaFX节点相关联

因此,在您的情况下,您只需将Person存储在形状

shape.setUserData(person);

并在鼠标悬停事件中检索它以显示数据:

(Person)shape.getUserData();