JavaFX - невозможно привести объект к SimpleListProperty - PullRequest
0 голосов
/ 11 сентября 2018

Я пытаюсь установить, есть ли способ загрузить ListProperty, например SimpleListProperty<InteractionDefinition>, из fxml без включения слоя FXCollections. Есть ли способ использовать функцию Case 1, как Case 2?

public class AppMenuItem extends MenuItem {
    private SimpleListProperty<InteractionDefinition> interactions = new SimpleListProperty<>();

    public void setInteractions(ObservableList<InteractionDefintion> interactionsTmp);
}

Дело 1

<AppMenuItem id="AppMenuItem2" menuText="View 2"  fx:id="asdDefaultView2MenuItem">
   <interactions>
        <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" />
   </interactions>
</AppMenuItem >

Дело 2

<AppMenuItem id="AppMenuItem2" menuText="View 2"  fx:id="asdDefaultView2MenuItem">
    <interactions>
        <FXCollections fx:factory="observableArrayList">
            <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" />
        </FXCollections>
    </interactions>
</AppMenuItem >

1 Ответ

0 голосов
/ 11 сентября 2018

Ответ

Проблема заключается в наличии метода установки. В разделе Элементы свойств документа Введение в 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 >
...