JavaFX-无法将对象强制转换为SimpleListProperty

时间:2018-09-11 04:01:28

标签: java javafx javafx-8

我正在尝试确定是否有一种方法可以从fxml加载ListProperty,例如SimpleListProperty<InteractionDefinition>,而不包括FXCollections层。有没有办法使案例1像案例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 :(得分:2)

答案

问题在于设置方法的存在。在 FXML简介文档的Property Elements部分,您将看到:

  

属性元素

     

其标签名称以小写字母开头的元素表示对象属性。属性元素可以表示以下其中一项:

     
      
  • 属性设置器
  •   
  • 只读列表属性
  •   
  • 只读地图属性
  •   
     

属性设置器

     

如果元素代表属性设置器,则该元素的内容(必须是文本节点或嵌套类实例元素)作为值传递给属性的设置器。

     

...

     

只读列表属性

     

只读列表属性是Bean属性,其getter返回java.util.List的实例,并且没有相应的setter方法。只读列表元素的内容在处理时会自动添加到列表中。

     

...

FXMLLoader处理<interactions>标签时,它将尝试使用标签内定义的对象来更新相应的属性。如何更新属性取决于上述文档中指定的规则-上面引用了其中的一些规则。由于您有一个setter方法(形式为setInteractions(ObservableList)),它将尝试将标签中定义的对象强制转换为ObservableList并使用setter方法;这是因为属性元素被确定为“属性设置器”,而不是“只读列表属性”。因此,情况1 失败,因为您没有定义ObservableList,而是定义了多个InteractionDefinition

如果要使用案例1 ,则需要将iteractions属性设置为只读,并添加一个getter方法。从FXMLLoader的角度来看,该属性仅需为只读。换句话说,只要您没有遵循标准Java Bean约定的setter方法,FXMLLoader就会假定该属性为只读。

至少,将代码更改为以下内容将允许您使用案例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属性定义元素。此属性不是“只读列表属性”,因为它定义了一个setter(因为itemsObjectProperty<ObservableList<T>>)。尽管如此,以下内容仍然有效:

<ListView>
    <String fx:value="Item #1"/>
</ListView>

由于有一个setter并且使用了ObservableList而不是String,所以您会期望得到强制错误。有趣的是,如果稍微更改FXML,您将 得到错误。

<ListView>
    <items>
        <String fx:value="Item #1"/>
    </items>
</ListView>

现在抛出一个错误。那有什么呢?我能发现的唯一区别是使用@DefaultProperty。如果指定了默认属性,并且您不要在FXML文件中显式使用元素标记(例如<items>),则会显示该属性,它的作用类似于“只读列表属性”( (如果默认属性是或包含List),而不是“属性设置器”。一旦您执行明确使用了元素标记(例如<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 >