从fxml实例化的ObservableArrayList的返回值与预期不符

时间:2014-10-28 17:26:59

标签: java javafx-2 fxml

我正在尝试将变量列表中的字符串传递给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说它“创建了一个可观察数组列表的实例,也填充了三个字符串值”。我是否遗漏了有关如何处理可观察列表的信息,或者这是预期的行为?

1 个答案:

答案 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> 的只读列表属性机制。