为什么我不能在cellFactory上指定NamedArgs和item元素?

时间:2016-02-16 10:01:23

标签: javafx tableview coercion

我在fxml中有以下tableView

<TableView fx:id="tableView" prefHeight="525.0" prefWidth="814.0">
  <columns>
    <TableColumn prefWidth="75.0">
      <graphic><ToggleButton fx:id="mainToggleButton" maxHeight="1.7976931348623157E308" maxWidth="1.7976931348623157E308" onAction="#onMainToggleButtonAction" text="Start all" /></graphic>
      <cellFactory><ToggleButtonTableCellFactory maxHeight="1.7976931348623157E308" maxWidth="1.7976931348623157E308" activatedText="Started" deactivatedText="Stopped"/></cellFactory>
      <cellValueFactory><PropertyValueFactory property="state"/></cellValueFactory>
    </TableColumn>
    <TableColumn prefWidth="75.0" text="Side">
      <cellValueFactory><PropertyValueFactory property="side"/></cellValueFactory>
    </TableColumn>
    <TableColumn prefWidth="75.0" text="Source">
      <cellValueFactory><PropertyValueFactory property="sourceContract"/></cellValueFactory>
    </TableColumn>
    <TableColumn prefWidth="75.0" text="Reference" editable="true">
      <cellFactory>
        <ChoiceBoxTableCellFactory maxHeight="1.7976931348623157E308" maxWidth="1.7976931348623157E308">
          <items>
            <FXCollections fx:factory="observableArrayList">
              <String fx:value="r01" />
              <String fx:value="r02" />
            </FXCollections>
          </items>
        </ChoiceBoxTableCellFactory>
      </cellFactory>
      <cellValueFactory><PropertyValueFactory property="referenceContract"/></cellValueFactory>
    </TableColumn>
    <TableColumn prefWidth="75.0" text="Destination">
      <cellValueFactory><PropertyValueFactory property="destinationContract"/></cellValueFactory>
    </TableColumn>
    <TableColumn prefWidth="75.0" text="Margin" editable="true">
      <cellFactory><SpinnerTableCellFactory maxHeight="1.7976931348623157E308" maxWidth="1.7976931348623157E308" min="0" max="1" initialValue="0" amountToStepBy="0.025" decimalFormat="0.000"/></cellFactory>
      <cellValueFactory><PropertyValueFactory property="margin"/></cellValueFactory>
    </TableColumn>
    <TableColumn prefWidth="75.0" text="Bot">
      <cellValueFactory><PropertyValueFactory property="bot"/></cellValueFactory>
    </TableColumn>
    <TableColumn prefWidth="75.0" text="Price">
      <cellValueFactory><PropertyValueFactory property="price"/></cellValueFactory>
    </TableColumn>
    <TableColumn prefWidth="75.0" text="Volume">
      <cellValueFactory><PropertyValueFactory property="volume"/></cellValueFactory>
    </TableColumn>
  </columns>
  <items>
    <FXCollections fx:factory="observableArrayList">
      <GridRowModel state="false" side="BID" sourceContract="s01" referenceContract="r01" destinationContract="d01" margin="0" bot="MinMax" price="15.125" volume="0" />
      <GridRowModel state="false" side="ASK" sourceContract="s02" referenceContract="r01" destinationContract="d02" margin="0" bot="MinMax" price="15.125" volume="0" />
    </FXCollections>
  </items>
</TableView>

ChoiceBoxTableCellFactory有一个构造函数,它接受来自fxml的命名参数和items元素的setter。

public class ChoiceBoxTableCellFactory<S, T> implements Callback<TableColumn<S, String>, TableCell<S, String>> {

  private ObservableList<String> items;
  private double maxHeight;
  private double maxWidth;

  public ChoiceBoxTableCellFactory() {
  }

  public ChoiceBoxTableCellFactory(
    @NamedArg("maxHeight") double maxHeight,
    @NamedArg("maxWidth") double maxWidth) {
    this.maxHeight = maxHeight;
    this.maxWidth = maxWidth;
  }

  @Override
  public TableCell<S, String> call(TableColumn<S, String> param) {
    return new TableCell<S, String>() {

      ChoiceBox<String> choiceBox = new ChoiceBox<>(getItems());

      {
        choiceBox.setMaxHeight(maxHeight);
        choiceBox.setMaxWidth(maxWidth);
        choiceBox.valueProperty().addListener((obs, oldValue, newValue) -> {
          ObservableValue<String> value = getTableColumn().getCellObservableValue(getIndex());
          if (value instanceof WritableValue) {
            ((WritableValue<String>) value).setValue(newValue);
          }
        });
      }

      @Override
      protected void updateItem(String item, boolean empty) {
        super.updateItem(item, empty);
        if (empty) {
          setText(null);
          setGraphic(null);
        } else {
          if (isEditing()) {
            setText(null);
            setGraphic(null);
          } else {
            choiceBox.setValue(item);
            setText(null);
            setGraphic(choiceBox);
          }
        }
      }
    };
  }

  public ObservableList<String> getItems() {
    return items;
  }

  public void setItems(ObservableList<String> items) {
    this.items = items;
  }
}

但是这引发了这个异常

Caused by: java.lang.IllegalArgumentException: Unable to coerce [[r01, r02]] to interface javafx.collections.ObservableList.
    at com.sun.javafx.fxml.BeanAdapter.coerce(BeanAdapter.java:496)
    at com.sun.javafx.fxml.builder.ProxyBuilder$Setter.invoke(ProxyBuilder.java:533)
    at com.sun.javafx.fxml.builder.ProxyBuilder.createObjectFromDefaultConstructor(ProxyBuilder.java:338)
    ... 144 more

当我删除maxHeightmaxWidth属性时,通过设置器正确设置了items字段。使用这些属性,items值将包含在另一个数组中。我怎样才能达到预期的效果?

1 个答案:

答案 0 :(得分:1)

看起来它应该有效。我尝试了一个更简单的测试,以下解决方法似乎在那里工作:

如果您在代码中的其他位置不需要setItems()方法,请将其删除并使用read only list properties方法。即完全删除setItems(...)方法,并在FXML中删除

    <ChoiceBoxTableCellFactory maxHeight="1.7976931348623157E308" maxWidth="1.7976931348623157E308">
      <items>
        <String fx:value="r01" />
        <String fx:value="r02" />
      </items>
    </ChoiceBoxTableCellFactory>

反过来似乎是使用<fx:define>块来定义项目,并使用属性对其进行初始化:

<fx:define>
    <FXCollections fx:factory="observableArrayList" fx:id="items">
        <String fx:value="r01"/>
        <String fx:value="r02"/>
    </FXCollections>
<fx:define>
<ChoiceBoxTableCellFactory maxHeight="1.7976931348623157E308" maxWidth="1.7976931348623157E308"
    items="$items" />