Ответ
Проблема заключается в наличии метода установки. В разделе Элементы свойств документа Введение в FXML вы увидите:
Элементы собственности
Элементы, имена тегов которых начинаются со строчной буквы, представляют свойства объекта. Элемент свойства может представлять один из следующих элементов:
- Установщик свойств
- Свойство списка, доступное только для чтения
- Свойство карты только для чтения
Собственность сеттеров
Если элемент представляет установщик свойства, содержимое элемента (которое должно быть либо текстовым узлом, либо вложенным элементом экземпляра класса) передается в качестве значения установщику для свойства.
...
Свойства списка только для чтения
Свойство списка, доступное только для чтения, является свойством Bean, метод получения которого возвращает экземпляр java.util.List
и не имеет соответствующего метода установки. Содержимое элемента списка, доступного только для чтения, автоматически добавляется в список по мере его обработки.
...
Когда FXMLLoader
обрабатывает тег <interactions>
, он пытается обновить соответствующее свойство объектами, определенными в теге. То, как оно обновляет свойство, подчиняется правилам, указанным в упомянутом документе, некоторые из которых приведены выше. Поскольку у вас есть метод установки (в форме setInteractions(ObservableList)
), он попытается привести объект, определенный в теге, к ObservableList
и использовать метод установки; это связано с тем, что элемент свойства определен как «установщик свойств», а не как «свойство списка только для чтения». Таким образом, Case 1 терпит неудачу, потому что вы не определяете ObservableList
, а скорее несколько InteractionDefinition
s.
Если вы хотите использовать Case 1 , вам нужно сделать свойство iteractions
доступным только для чтения и добавить метод получения. Свойство должно быть только для чтения с точки зрения FXMLLoader
. Другими словами, если у вас нет метода установки, соответствующего стандартным соглашениям Java Bean, FXMLLoader
будет предполагать, что свойство доступно только для чтения.
Минимально, изменив код на следующий, вы сможете использовать Case 1 :
public class AppMenuItem extends MenuItem {
private SimpleListProperty<InteractionDefinition> interactions = new SimpleListProperty<>();
public ObservableList<InteractionDefinition> getInteractions() {
return iteractions.get();
}
}
Если вы решили сделать свойство полностью доступным только для чтения, вы можете использовать ReadOnlyListWrapper
. Или, если вам не нужно полноценное свойство JavaFX, вы можете просто использовать ObservableList
напрямую.
Противоречия
Однако есть нечто, что противоречит всему сказанному. Если вы определяете ListView
, например, в FXML, вы также можете определить элементы для его свойства items
. Это свойство не является «списочным свойством только для чтения», поскольку оно определяет установщик (поскольку items
является ObjectProperty<ObservableList<T>>
). Несмотря на это, ниже все еще работает:
<ListView>
<String fx:value="Item #1"/>
</ListView>
Вы ожидаете получить ошибку приведения, так как есть установщик, и он принимает ObservableList
, а не String
. Интересно, что если вы немного измените FXML, вы получите ошибку.
<ListView>
<items>
<String fx:value="Item #1"/>
</items>
</ListView>
Теперь выдается ошибка. Так что же дает? Единственное отличие, которое я могу заметить, это использование @DefaultProperty
. Появляется, если указано свойство по умолчанию, и вы не явно не используете тег элемента (например, <items>
) в файле FXML, тогда оно будет вести себя как «свойство списка только для чтения» (если по умолчанию свойство является или содержит List
), а не «установщик свойства». Как только вы do явно используете тег элемента (например, <items>
), он ведет себя так, как описано в первой части этого ответа. Я не могу найти документацию, явно описывающую это поведение, что может означать, что это ошибка. Также обратите внимание, что я пробовал это только на Java 10.0.2.
Если вы хотите положиться на это, и поскольку MenuItem
не имеет DefaultProperty
, вы можете попробовать использовать следующее:
Java-код:
@DefaultProperty("interactions")
public class AppMenuItem extends MenuItem {
private final ListProperty<InteractionDefintion> interactions = new SimpleListProperty<>(this, "interactions");
public final void setInteractions(ObservableList<InteractionDefinition> interactions) {
this.interactions.set(interactions);
}
public final ObservableList<InteractionDefinition> getInteractions() {
return interactions.get();
}
public final ListProperty<InteractionDefinition> interactionsProperty() {
return interactions;
}
}
Файл FXML:
<AppMenuItem id="AppMenuItem2" menuText="View 2" fx:id="asdDefaultView2MenuItem">
<InteractionDefinition action="actionName" button="ACTION" device="MOUSE" event="CLICK" />
<InteractionDefinition action="actionName" button="ACTION" device="MOUSE" event="CLICK" />
<InteractionDefinition action="actionName" button="ACTION" device="MOUSE" event="CLICK" />
</AppMenuItem >