我正在尝试将变量列表中的字符串传递给fxml中的自定义控制器,因此:
<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说它“创建了一个可观察数组列表的实例,也填充了三个字符串值”。我是否遗漏了有关如何处理可观察列表的信息,或者这是预期的行为?
答案 0 :(得分:3)
我怀疑您的StepChart
课程没有setStatistics(...)
方法。 FXMLLoader
将以特殊方式处理此问题,如下所示:
通常,当FXMLLoader
遇到如下结构时:
<ClassName fx:id="ref">
<propertyName>
<AnotherClass />
</propertyName>
</ClassName>
它执行的代码转换为:
ClassName ref = new ClassName();
ref.setPropertyName(new AnotherClass());
(不是字面意思,它相同但使用反射)。
在大多数情况下,如果指定的类中没有定义setPropertyName(...)
,则会抛出异常。
只读列表被视为一种特殊情况:因此,如果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>
的所有直接子元素,并将每个元素传递给{{ 1}}在它创建的getStatistics().add(...)
实例上调用。在这种情况下,只有一个子元素,即StepChart
。
ObservableList<String>
也足够聪明,可以在运行时计算列表的泛型类型(我实际上不知道它是如何做到的)。由于它看到列表期望FXMLLoader
,并且该元素未解析为String
,因此它会尝试将其强制转换为正确的类型。强制转换为String
非常简单:它只会在创建列表的结果上调用String
,并将结果传入。所以最终会得到一个包含单个toString
的列表: String
是在String
上调用toString
的结果,其中包含元素&#34;员工&#34;和#34;农场&#34;。等效的Java代码看起来像
List
因此您会看到单个元素StepChart employment = new StepChart();
employment.getStatistics().add(Arrays.asList("employees", "farms").toString());
作为"["employees", "farms"]"
的内容。
您有两个针对此问题的修复程序,您选择的修复程序应取决于您要在statistics
中公开的API。您可以添加StepChart
方法。然后setStatistics(ObservableList<String>)
将创建列表,并将其传递给该方法。
或者,您可以继续省略FXMLLoader
方法(即将setStatistics(...)
保留为只读列表属性)并将FXML修改为
statistics
以便您使用 <StepChart fx:id="employment" ... >
<statistics>
<String fx:value="employees"/>
<String fx:value="farms"/>
</statistics>
</StepChart>
的只读列表属性机制。