Здесь это другое решение, которое, по моему мнению, гораздо более надежное, чем использование принципиально нетипизированных userData
или properties
map. Используя любой из этих подходов будет либо загромождать ваш код с проверками на null
, и для вещей, которые являются правильным типом, или заставит вас предположить, что код по всему вашему приложению использует элементы интерфейса правильно. Первому из них будет намного сложнее читать (и, следовательно, поддерживать) ваш код. Второе означает, что ваш код уязвим для ошибок, которые трудно отследить (потому что они встречаются в коде, который может быть далеко от кода, где они материализуются).
Либо вы создаете свои элементы пользовательского интерфейса, подклассифицируя их, либо у вас есть способ (или некоторый блок кода) для их создания. В первом случае, вы просто дать подклассу ссылку на данные ему необходимо:
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()));
, и это также просто, чтобы добавить другие функциональные возможности пользовательского интерфейса:
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));
}
Вы должны размещать код, что вы пробовали до сих пор. – Tobi