JavaFX无法在枚举与自定义单元工厂一起使用时设置数据

时间:2017-01-11 12:29:37

标签: java javafx java-8 javafx-2 javafx-8

[附全源代码]

我有一个包含3列的javafx TableView(“名称”,“性别”,“国家/地区”)。我正在将细胞工厂附加到我的所有栏目中。如下所示。

nameCol.setCellFactory(TextFieldTableCell.forTableColumn());
genderCol.setCellFactory(ComboBoxTableCell.forTableColumn(GenderEnum.values()));
countryCol.setCellFactory(ComboBoxTableCell.forTableColumn("India", "USA"));

我没有自己实现任何提交逻辑。现在我更改所有3列中的值,然后打印数据。我能够在除genderCol之外的所有列上看到更改的值。关于该列的唯一区别是它使用枚举。

当我更改countryCol(也是ComoboBoxTableCell)中的值时,countryProperty首先使用旧值调用两次,然后使用新值调用,但对于genderCol,genderProperty仅调用一次。

您可以复制并执行代码,首先打印数据而不更改任何内容,然后更改所有3列上的值,然后打印数据。现在检查,除了genderCol,其他列的值已更改。

我遗漏了任何内容或对枚举做错了什么

Main.java

package application;

import javafx.application.Application;
import javafx.fxml.FXMLLoader;
import javafx.scene.Scene;
import javafx.scene.layout.VBox;
import javafx.stage.Stage;

public class Main extends Application
{
    @Override
    public void start(final Stage primaryStage)
    {
        try
        {
            final VBox root = FXMLLoader.load(this.getClass().getResource("MainView.fxml"));
            final Scene scene = new Scene(root, 400, 400);
            primaryStage.setScene(scene);
            primaryStage.show();
        }
        catch(final Exception e)
        {
            e.printStackTrace();
        }
    }

    public static void main(final String[] args)
    {
        launch(args);
    }
}

MainView.fxml

<?xml version="1.0" encoding="UTF-8"?>

<?import javafx.scene.control.Button?>
<?import javafx.scene.control.TableColumn?>
<?import javafx.scene.control.TableView?>
<?import javafx.scene.layout.VBox?>
<?import javafx.scene.control.cell.PropertyValueFactory?>

<VBox spacing="10.0" xmlns="http://javafx.com/javafx/8.0.65" xmlns:fx="http://javafx.com/fxml/1" fx:controller="application.MainController">
   <children>
      <TableView fx:id="table" editable="true">
        <columns>
          <TableColumn prefWidth="100.0" text="Name" fx:id="nameCol">
          <cellValueFactory><PropertyValueFactory property="name" /></cellValueFactory>
          </TableColumn>
          <TableColumn prefWidth="100.0" text="Gender" fx:id="genderCol">
          <cellValueFactory><PropertyValueFactory property="gender" /></cellValueFactory>
          </TableColumn>
          <TableColumn prefWidth="100.0" text="Country" fx:id="countryCol">
          <cellValueFactory><PropertyValueFactory property="country" /></cellValueFactory>
          </TableColumn>
        </columns>
      </TableView>
      <Button text="Print Data"  onAction="#printData"/>
   </children>
</VBox>

MainController.java

package application;

import java.net.URL;
import java.util.ResourceBundle;

import application.Person.GenderEnum;
import javafx.collections.FXCollections;
import javafx.fxml.FXML;
import javafx.fxml.Initializable;
import javafx.scene.control.TableColumn;
import javafx.scene.control.TableColumn.CellEditEvent;
import javafx.scene.control.TableView;
import javafx.scene.control.cell.ComboBoxTableCell;
import javafx.scene.control.cell.TextFieldTableCell;

/**
 * @author Saravana Kumar M
 *
 */
public class MainController implements Initializable
{
    @FXML
    TableView<Person> table;
    @FXML
    TableColumn<Person, String> nameCol;
    @FXML
    TableColumn<Person, GenderEnum> genderCol;
    @FXML
    TableColumn<Person, String> countryCol;

    @Override
    public void initialize(final URL location, final ResourceBundle resources)
    {
        this.nameCol.setCellFactory(TextFieldTableCell.forTableColumn());
        this.genderCol.setCellFactory(ComboBoxTableCell.forTableColumn(GenderEnum.values()));
        this.countryCol.setCellFactory(ComboBoxTableCell.forTableColumn("India", "USA"));

        final Person p1 = new Person("A", "M", "India");
        final Person p2 = new Person("B", "F", "USA");
        final Person p3 = new Person("C", "N", "India");
        final Person p4 = new Person("D", null, "USA");
        final Person p5 = new Person("E", "M", "India");
        final Person p6 = new Person("F", "F", "USA");
        final Person p7 = new Person("G", "N", "India");
        final Person p8 = new Person("H", null, "USA");

        this.table.setItems(FXCollections.observableArrayList(p1, p2, p3, p4, p5, p6, p7, p8));

        this.genderCol.setOnEditCommit((event) -> this.doThis(event));
    }

    private void doThis(final CellEditEvent<Person, GenderEnum> event)
    {
        System.out.println(event);
        System.out.println(event.getOldValue());
        System.out.println(event.getNewValue());
        System.out.println(event.getRowValue());
    }

    @FXML
    public void printData()
    {
        this.table.getItems().forEach(System.out::println);
    }
}

Person.java

package application;

import javafx.beans.property.ObjectProperty;
import javafx.beans.property.SimpleObjectProperty;
import javafx.beans.property.SimpleStringProperty;
import javafx.beans.property.StringProperty;

/**
 * @author Saravana Kumar M
 *
 */
public class Person
{
    private final StringProperty name;
    private final ObjectProperty<GenderEnum> gender;
    private final StringProperty country;

    public Person(final String name, final String gender, final String country)
    {
        this.name = new SimpleStringProperty(name);

        if(gender == null)
        {
            this.gender = new SimpleObjectProperty<>();
        }
        else
        {
            this.gender = new SimpleObjectProperty<>(GenderEnum.valueOf(gender));
        }

        this.country = new SimpleStringProperty(country);
    }

    public String getName()
    {
        return this.name.get();
    }

    public void setName(final String name)
    {
        this.name.set(name);
    }

    public StringProperty nameProperty()
    {
        return this.name;
    }

    public GenderEnum getGender()
    {
        return this.gender.get();
    }

    public void setGender(final GenderEnum gender)
    {
        this.gender.set(gender);
    }

    public ObjectProperty<GenderEnum> genderProperty()
    {
        return this.gender;
    }

    public String getCountry()
    {
        return this.country.get();
    }

    public void setCountry(final String country)
    {
        this.country.set(country);
    }

    public StringProperty countryProperty()
    {
        return this.country;
    }

    @Override
    public String toString()
    {
        return "name : " + this.name + " name.get : " + this.name.get() + " gender : " + this.gender + " gender.get : " + this.gender.get() + " country : " + this.country.get() + " country.get : " + this.country.get();
    }

    public enum GenderEnum
    {
        M("Male"), F("Female"), N("None");

        private final String label;
        private GenderEnum(final String label)
        {
            this.label = label;
        }

        @Override
        public String toString()
        {
            return this.label;
        }
    }
}

先谢谢。

1 个答案:

答案 0 :(得分:2)

来自TableView documentation

  

默认情况下,TableColumn编辑提交处理程序为非null,带有   尝试覆盖的属性值的默认处理程序   当前正在编辑的行中的项目。它可以做到这一点   Cell.commitEdit(Object)方法在新值中传递,这是   通过CellEditEvent传递给编辑提交处理程序   被解雇。这只是一个呼唤的问题   TableColumn.CellEditEvent.getNewValue()要检索此值。

     

非常重要的是要注意,如果你打电话   TableColumn.setOnEditCommit(javafx.event.EventHandler)与您自己   EventHandler,然后您将删除默认处理程序。除非   然后处理对属性的回写(或相关数据)   来源),什么都不会发生。您可以使用以下方法解决此问题   TableColumnBase.addEventHandler(javafx.event.EventType, javafx.event.EventHandler)方法添加   带有所需EventHandler的TableColumn.EDIT_COMMIT_EVENT EventType   作为第二个论点。使用此方法,您将不会替换   默认实现,但在编辑提交时会通知您   已经发生了。

因为你有

    this.genderCol.setOnEditCommit((event) -> this.doThis(event));

在控制器类中,删除更新属性的默认处理程序。如果用

替换它
    this.genderCol.addEventHandler(TableColumn.<Person, GenderEnum>editCommitEvent(),
        event -> this.doThis(event));

它将按预期工作。 (注意文档在告诉您如何获取事件类型时不正确。另请注意,您的处理程序将在默认值之前调用,因此getRowValue()的输出将在更新之前显示值 ,但按下按钮显示值将给出新值。)