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

Why can't I specify NamedArgs and item elements on cellFactory?

我在 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 属性时,项目字段已通过 setter 正确设置。使用这些属性,项目值被包装在一个额外的数组中。我怎样才能达到预期的效果?

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

如果您的代码中的其他任何地方都不需要 setItems() 方法,请将其删除并使用 read only list properties 方法。 IE。完全删除 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" />