JavaFX - 无法将Object强制转换为SimpleListProperty

问题描述 投票:2回答:1

我试图确定是否有办法从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 >
java javafx javafx-8
1个回答
2
投票

Answer

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

Property Elements

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

  • 属性设定者
  • 只读列表属性
  • 只读地图属性

Property Setters

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

...

Read-Only List Properties

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

...

FXMLLoader处理<interactions>标记时,它将尝试使用标记内定义的对象更新相应的属性。它如何更新财产受到上述文件中规定的规则的约束 - 其中一些在上面引用。由于你有一个setter方法(以setInteractions(ObservableList)的形式),它会尝试将标签中定义的对象强制转换为ObservableList并使用setter方法;这是因为属性元素被确定为“属性设置器”而不是“只读列表属性”。因此,案例1失败是因为你没有定义ObservableList而是多个InteractionDefinitions。

如果你想使用Case 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


Contradictions?

然而,有些东西似乎与所说的一切相矛盾。例如,如果你定义一个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 >
© www.soinside.com 2019 - 2024. All rights reserved.