2012-09-26 129 views
0

我在使用JavaFX 2 TableView处理数据中的空值时出现严重问题。事实上我遇到了很多麻烦,下面显示了一个简单的问题演示。处理单元格中的空值

本质的问题是,我的一些数据可能为空,强迫空的值,如一个空字符串,是无效的,它必须为空。在我正在处理的实际代码中,我有一个空日期值,以保持示例简单,我在下面显示了一个空字符串。

表的第1行和第2行有空值。这两列是不同的,第一列显示TextFieldTableCell的行为,第二是我实现可编辑单元格的行为。两者都显示相同的错误行为。

当前的行为是这样的:

  1. 单击单元格进入编辑模式
  2. 在单元格中输入一个值
  3. 按回车键提交编辑
  4. 什么也没有发生

在第4步,我会希望列的onEditCommit处理程序被调用,但它不是。有一个看看源javax.scene.control.TableCell提交,因为的commitEdit的第一行是不会发生的是:

if (! isEditing()) return; 

看来,由于电池是永远不会被设置为null编辑属性虽然我承认我还没有追踪所有的代码,看看为什么它永远不会被切换到真实。

一如既往地感谢您的帮助。


主要应用

package example; 

import javafx.application.Application; 
import javafx.collections.FXCollections; 
import javafx.collections.ObservableList; 
import javafx.event.EventHandler; 
import javafx.scene.Group; 
import javafx.scene.Scene; 
import javafx.scene.control.TableCell; 
import javafx.scene.control.TableColumn; 
import javafx.scene.control.TableView; 
import javafx.scene.control.cell.PropertyValueFactory; 
import javafx.scene.control.cell.TextFieldTableCell; 
import javafx.stage.Stage; 
import javafx.util.Callback; 

public class NullCellEditingExample extends Application { 

    private TableView table = new TableView(); 
    private final ObservableList<Person> data = 
      FXCollections.observableArrayList(new Person(null, "Smith"), new Person("Isabella", null), 
      new Person("Ethan", "Williams"), new Person("Emma", "Jones"), new Person("Michael", "Brown")); 

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

    @Override 
    public void start(Stage stage) { 
     Scene scene = new Scene(new Group()); 

     TableColumn firstNameCol = createSimpleFirstNameColumn(); 
     TableColumn lastNameCol = createLastNameColumn(); 
     table.setItems(data); 
     table.getColumns().addAll(firstNameCol, lastNameCol); 
     table.setEditable(true); 

     ((Group) scene.getRoot()).getChildren().addAll(table); 
     stage.setScene(scene); 
     stage.show(); 
    } 

    private TableColumn createSimpleFirstNameColumn() { 
     TableColumn firstNameCol = new TableColumn("First Name"); 
     firstNameCol.setMinWidth(100); 
     firstNameCol.setCellValueFactory(new PropertyValueFactory<Person, String>("firstName")); 
     firstNameCol.setCellFactory(TextFieldTableCell.forTableColumn()); 
     firstNameCol.setOnEditCommit(new EventHandler<TableColumn.CellEditEvent<Person, String>>() { 
      @Override 
      public void handle(TableColumn.CellEditEvent<Person, String> t) { 
       ((Person) t.getTableView().getItems().get(t.getTablePosition().getRow())).setFirstName(t.getNewValue()); 
      } 
     }); 

     return firstNameCol; 
    } 

    private TableColumn createLastNameColumn() { 
     Callback<TableColumn, TableCell> editableFactory = new Callback<TableColumn, TableCell>() { 
      @Override 
      public TableCell call(TableColumn p) { 
       return new EditingCell(); 
      } 
     }; 

     TableColumn lastNameCol = new TableColumn("Last Name"); 
     lastNameCol.setMinWidth(100); 
     lastNameCol.setCellValueFactory(new PropertyValueFactory<Person, String>("lastName")); 
     lastNameCol.setCellFactory(editableFactory); 
     lastNameCol.setOnEditCommit(new EventHandler<TableColumn.CellEditEvent<Person, String>>() { 
      @Override 
      public void handle(TableColumn.CellEditEvent<Person, String> t) { 
       t.getRowValue().setLastName(t.getNewValue()); 
      } 
     }); 

     return lastNameCol; 
    } 
} 

编辑的单元格

package example; 

import javafx.beans.value.ChangeListener; 
import javafx.beans.value.ObservableValue; 
import javafx.event.EventHandler; 
import javafx.scene.control.TableCell; 
import javafx.scene.control.TextField; 
import javafx.scene.input.KeyCode; 
import javafx.scene.input.KeyEvent; 

public class EditingCell extends TableCell<Person, String> { 

    private TextField textField; 

    public EditingCell() { 
    } 

    @Override 
    public void startEdit() { 
     super.startEdit(); 
     if(textField == null) { 
      createTextField(); 
     } 
     setText(null); 
     setGraphic(textField); 
     textField.selectAll(); 
    } 

    @Override 
    public void cancelEdit() { 
     super.cancelEdit(); 
     setText((String) getItem()); 
     setGraphic(null); 
    } 

    @Override 
    public void updateItem(String item, boolean empty) { 
     super.updateItem(item, empty); 
     if (empty) { 
      setText(null); 
      setGraphic(null); 
     } else { 
      if (isEditing()) { 
       if (textField != null) { 
        textField.setText(getString()); 
       } 
       setText(null); 
       setGraphic(textField); 
      } else { 
       setText(getString()); 
       setGraphic(null); 
      } 
     } 
    } 

    private void createTextField() { 
     textField = new TextField(getString()); 
     textField.setMinWidth(this.getWidth() - this.getGraphicTextGap() * 2); 
     textField.focusedProperty().addListener(new ChangeListener<Boolean>() { 
      @Override 
      public void changed(ObservableValue<? extends Boolean> arg0, Boolean arg1, Boolean arg2) { 
       if (!arg2) { commitEdit(textField.getText()); } 
      } 
     }); 

     textField.setOnKeyReleased(new EventHandler<KeyEvent>() { 
      @Override 
      public void handle(KeyEvent t) { 
       if (t.getCode() == KeyCode.ENTER) { 
        String value = textField.getText(); 
        if (value != null) { commitEdit(value); } else { commitEdit(null); } 
       } else if (t.getCode() == KeyCode.ESCAPE) { 
        cancelEdit(); 
       } 
      } 
     }); 
    } 

    private String getString() { 
     return getItem() == null ? "" : getItem().toString(); 
    } 
} 

package example; 

import javafx.beans.property.SimpleStringProperty; 

public class Person { 

    private final SimpleStringProperty firstName; 
    private final SimpleStringProperty lastName; 

    public Person(String firstName, String lastName) { 
     this.firstName = new SimpleStringProperty(firstName); 
     this.lastName = new SimpleStringProperty(lastName); 
    } 

    public String getFirstName() { 
     return firstName.get(); 
    } 

    public void setFirstName(String firstName) { 
     this.firstName.set(firstName); 
    } 

    public SimpleStringProperty firstNameProperty() { 
     return firstName; 
    } 

    public String getLastName() { 
     return lastName.get(); 
    } 

    public void setLastName(String lastName) { 
     this.lastName.set(lastName); 
    } 

    public SimpleStringProperty lastNameProperty() { 
     return lastName; 
    } 
} 

回答

2

自发布以来,我发现another question基本相同。他们采取了总是避免空值的方法,这对于字符串来说很好(例如,使用空字符串),但对于日期或其他没有明显“空”值的数据类型而言不可接受。

的解决方案是假值传递到在EditingCell.updateItem方法super.updateItem呼叫。如果有人对完整分析感兴趣,我已经编制了一个full write up