我正在尝试将字符串变量列表传递到 fxml 中的自定义 Controller 中,因此:
<Pane xmlns:fx="http://javafx.com/fxml" >
<VBox fx:id="vbox">
<StepChart fx:id="employment" title="Unemployment" enabled = "true" prefHeight="250.0" prefWidth="300.0">
<statistics>
<FXCollections fx:factory="observableArrayList" >
<String fx:value="employees"/>
<String fx:value="farms"/>
</FXCollections>
</statistics>
</StepChart>
</VBox>
</Pane>
统计信息在 StepChart 中被标记为 @DefaultProperty,但为了完整性而包含在此处。在 StepChart 中,我选取可观察列表值,如下所示:
ArrayList<String> list = new ArrayList<String>();
ObservableList<String> statistics = FXCollections.observableArrayList(list);
statistics.addListener(new ListChangeListener<String>()
{
@Override
public void onChanged(ListChangeListener.Change<? extends String> change)
{
for(String s: statistics)
{
System.out.println("s: " + s);
}
}
});
一切都很完美,除了 fxml 生成的是单个字符串,而不是单独的字符串列表之外,其中包含 fxml 值:
s: [employees, farms]
尽管Introduction to fxml说它“创建一个可观察数组列表的实例,填充三个字符串值”。我是否遗漏了有关如何处理可观察列表的信息,或者这是预期的行为?
最佳答案
我怀疑你的StepChart
类没有 setStatistics(...)
方法。 FXMLLoader
会以特殊的方式处理这个问题,如下:
通常,当 FXMLLoader
遇到这样的结构:
<ClassName fx:id="ref">
<propertyName>
<AnotherClass />
</propertyName>
</ClassName>
它执行的代码会转换为以下内容:
ClassName ref = new ClassName();
ref.setPropertyName(new AnotherClass());
(不是字面上的意思,它执行等效操作,但使用反射)。
在大多数情况下,如果没有 setPropertyName(...)
定义在指定的类中,将会抛出异常。
只读列表被视为特殊情况:所以 if ClassName
定义了 List getList() {...}
方法,没有 void setList(List aList) {...}
方法,那么以下仍然有效:
<ClassName fx:id="ref">
<list>
<!-- values -->
</list>
</ClassName>
处理这种情况的方式,在这种特殊情况下(属性的名称对应于 List
类型的属性,有 get
方法,但没有 set
方法),然后是 FXMLLoader
执行以下伪代码:
ClassName ref = new ClassName();
for (Object item : itemsDefinedInsideListElement) {
ref.getList().add(item);
}
其中 for
循环迭代 <list>
中定义的所有元素元素。这使您能够做类似的事情
<HBox>
<children>
<Label/>
<TextField/>
</children>
</HBox>
尽管HBox
没有setChildren()
方法。
因此,对于您的 FXML,假设 StepChart
没有setStatistics(...)
方法,FXMLLoader
迭代 <statistics>
的所有直接子元素,将每一个传递给 getStatistics().add(...)
在 StepChart
上调用它创建的实例。在这种情况下,只有一个子元素,即 ObservableList<String>
.
FXMLLoader
也足够聪明,可以在运行时计算列表的通用类型(我实际上不知道它是如何做到这一点的)。因为它看到列表期待 String
,并且该元素未解析为 String
,它尝试将其强制为正确的类型。强制为 String
很简单:它只需要调用 toString
因此,您最终会得到一个包含单个 String
的列表。 :那个String
是调用 toString
的结果在List
上包含元素“雇员”和“农场”。等效的 Java 代码类似于
StepChart employment = new StepChart();
employment.getStatistics().add(Arrays.asList("employees", "farms").toString());
因此您会看到单个元素 "["employees", "farms"]"
作为 statistics
的内容.
对此您有两个修复方案,您选择的修复方案应取决于您想要在 StepChart
中公开的 API 。您可以添加 setStatistics(ObservableList<String>)
方法。然后FXMLLoader
将创建列表,并将其传递给该方法。
或者,您可以继续省略 setStatistics(...)
方法(即保留 statistics
作为只读列表属性)并将 FXML 修改为
<StepChart fx:id="employment" ... >
<statistics>
<String fx:value="employees"/>
<String fx:value="farms"/>
</statistics>
</StepChart>
以便您使用FXMLLoader
的只读列表属性机制。
关于java - 从 fxml 实例化的 ObservableArrayList 返回值不符合预期,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/26614677/