Этот пост посвящен тому, как работать с JavaFX ListViews и TableViews и как эти элементы управления получают информацию об измененном содержимом содержащихся элементов. Интересно, почему я не нашел ничего о следующем паттерне в соответствующих книгах, так как это действительно важный механизм. Во многих публикациях предлагается принудительно вызвать ChangeEvent, чтобы получить ListView для обновления путем вызова:
list.remove(POJO);
list.add(index,POJO);
после каждого внесенного изменения! Бррр!
Но есть гораздо лучший способ:
Включите ваш список, чтобы сообщить об изменениях элемента, предоставив экстрактор свойств.
Демо-приложение
Я создал небольшое демонстрационное приложение, чтобы попробовать его. В основном два TableViews и один ListView все совместно используют одни и те же данные. Чтобы изменить свойства элементов, один TableView доступен для редактирования:
Модель данных
Компульсивный PersonBean, свертывающий JavaFX Bean Pattern / Convention
|
01
02
03
04
05
06
07
08
09
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
|
public class PersonBean { private StringProperty firstName; private StringProperty lastName; private ObjectProperty<LocalDate> birthday; private ObjectBinding<Long> age; public PersonBean() { } public PersonBean(String firstName, String lastName, LocalDate birthday) { setFirstName(firstName); setLastName(lastName); setBirthday(birthday); } public final StringProperty firstNameProperty() { if (firstName == null) { firstName = new SimpleStringProperty(); } return firstName; } public final String getFirstName() { return firstNameProperty().get(); } public final void setFirstName(final java.lang.String firstName) { firstNameProperty().set(firstName); } public final StringProperty lastNameProperty() { if (lastName == null) { lastName = new SimpleStringProperty(); } return lastName; } public final java.lang.String getLastName() { return lastNameProperty().get(); } public final void setLastName(final java.lang.String lastName) { lastNameProperty().set(lastName); } public final ObjectProperty<LocalDate> birthdayProperty() { if (birthday == null) { birthday = new SimpleObjectProperty<>(); } return birthday; } public final LocalDate getBirthday() { return birthdayProperty().get(); } public final void setBirthday(final java.time.LocalDate birthday) { birthdayProperty().set(birthday); } public String stringValue() { return String.format("%s %s %s", getFirstName(), getLastName(), getBirthday().format(DateTimeFormatter.ISO_LOCAL_DATE)); } public final ObjectBinding<Long> ageBinding() { if (age == null) { age = new ObjectBinding<Long>() { { bind(birthdayProperty()); } @Override protected Long computeValue() { if (getBirthday() == null) { return null; } return getBirthday().until(LocalDate.now(), ChronoUnit.YEARS); } }; } return age; } public static Callback<PersonBean, Observable[]> extractor() { return (PersonBean p) -> new Observable[]{p.lastNameProperty(), p.firstNameProperty(), p.birthdayProperty(), p.ageBinding()}; }} |
DataModel, содержащий список случайно созданных PersonBeans:
|
01
02
03
04
05
06
07
08
09
10
11
12
13
14
15
16
|
public class DataModel { private ObservableList<PersonBean> personFXBeans; public DataModel() { init(); } private void init() { personFXBeans = DataSource.getRandomPersonBeansList(100); } public ObservableList<PersonBean> getPersonFXBeans() { return personFXBeans; }} |
Как вы, возможно, знаете, чтобы назначить DataModel, например, для TableView или ListView в JavaFX, вам просто нужно использовать метод setItems (ObvervableList).
|
1
2
3
4
5
6
|
@FXMLpublic void onFillWithDemoDataFXBeans() { readOnlyListView.setItems(model.getPersonFXBeans()); readOnlyTableView.setItems(model.getPersonFXBeans()); editableTableView.setItems(model.getPersonFXBeans());} |
Теперь получение TableView для информирования об изменениях свойств содержащихся элементов уже выполняется с помощью привязки двумя способами: с помощью PropertyValueFactory и с помощью более или менее прямой привязки свойства:
|
1
2
3
4
5
6
7
8
9
|
readOnlyFirstNameColumn.setCellValueFactory(new PropertyValueFactory<>("firstName"));readOnlyLastNameColumn.setCellValueFactory(new PropertyValueFactory<>("lastName"));readOnlyBirthdayColumn.setCellValueFactory(new PropertyValueFactory<>("birthday"));readOnlyAgeColumn.setCellValueFactory(i -> i.getValue().ageBinding());editableFirstNameColumn.setCellValueFactory(i -> i.getValue().firstNameProperty());editableLastNameColumn.setCellValueFactory(i -> i.getValue().lastNameProperty());editableBirthdayColumn.setCellValueFactory(i -> i.getValue().birthdayProperty());ageColumn.setCellValueFactory(i -> i.getValue().ageBinding()); |
Но ListView в основном наблюдает только за списком, а не за свойствами каждого элемента этого списка.
При использовании ObservableList, созданного с помощью FXCollections.observableArrayList (), ListView будет обновляться только для событий ListChange, таких как remove () и add () элементов. Следовательно:
list.remove(POJO);
list.add(index,POJO);
после каждого внесенного изменения.
Но есть гораздо лучший способ:
Включите ваш список, чтобы сообщить об изменениях элемента, предоставив экстрактор свойств. Вам не нужно заботиться об обновлении!
ObservableList people = FXCollections.observableArrayList ( PersonBean.extractor () );
Смотрите DataSource.getRandomPersonBeansList(int length) :
|
1
2
3
4
5
6
7
|
public static ObservableList<PersonBean> getRandomPersonBeansList(int length) { ObservableList<PersonBean> persons = FXCollections.observableArrayList(PersonBean.extractor()); for (int i = 0; i < length; i++) { persons.add(new PersonBean(getRandomName(), getRandomLastname(), getRandomLocalDate())); } return persons; } |
Этот экстрактор в основном является обратным вызовом, содержащим массив Obvervables, которые затем наблюдаются Obervablelist (более точно: ObservableListWrapper):
Мой PersonBean уже обеспечивает обратный вызов извлечения:
|
1
2
3
|
public static Callback<PersonBean, Observable[]> extractor() { return (PersonBean p) -> new Observable[]{p.lastNameProperty(), p.firstNameProperty(), p.birthdayProperty(), p.ageBinding()};} |
В соответствии с этим шаблоном все элементы управления обновляются сразу после применения изменения.
Редактировать данные …
и совершить:
КОД, ПОЖАЛУЙСТА!
Вы можете найти полный код в моем BitBucket Repo .
| Ссылка: | Извлечение свойств: Лучший способ получить ListView, мгновенно обновляющий свои элементы от нашего партнера JCG Дженса Детерса в блоге JavaFX Delight . |


