我正在考虑如何以一种好的方式做到这一点。 我创建了一个程序,在GUI中显示一些自定义形状。现在当我将鼠标悬停在一个元素上时,我想显示一些与该元素相关的信息。 在这个例子中,让我们想象每个元素都是一个人,所以我想在鼠标悬停时显示一些关于那个人的基本信息(姓名,出生日期,性别......)。
如何将关于某个人的数据连接到元素数据代表该人?
据我所知,有办法做到这一点。
答案 0 :(得分:0)
这是一个不同的解决方案,我认为它比使用基本上无类型的userData
或properties
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()
因此,在您的情况下,您只需将Person
存储在形状
shape.setUserData(person);
并在鼠标悬停事件中检索它以显示数据:
(Person)shape.getUserData();