Reputation: 36179
I try to make table cells editable. I managed to do this with two columns that have String values in it, but I can't make this with columns that represent Integer values.
Places with X is where compiler get the error:
The method setCellFactory(Callback<TableColumn<DataModel,Integer>,TableCell<DataModel,Integer>>) in the type TableColumn<DataModel,Integer> is not applicable for the arguments (Callback<TableColumn<DataModel,String>,TableCell<DataModel,String>>)
and places with XX is where compiler get the error:
The method setOnEditCommit(EventHandler<TableColumn.CellEditEvent<DataModel,Integer>>) in the type TableColumn<DataModel,Integer> is not applicable for the arguments ((CellEditEvent<DataModel, Integer> event) -> {})
Here's the code:
public void initialize(URL location, ResourceBundle resources)
{
//Tworzymy sobie kolumny, które będą odpowiadać oraz przyjmować konretne dane
TableColumn<DataModel, String> nameColumn = new TableColumn<DataModel, String>("Name");
nameColumn.setMinWidth(100);
TableColumn<DataModel, String> surnameColumn = new TableColumn<DataModel, String>("Surname");
surnameColumn.setMinWidth(100);
TableColumn<DataModel, Integer> ageColumn = new TableColumn<DataModel, Integer>("Age");
ageColumn.setMinWidth(100);
TableColumn<DataModel, Integer> telNumberColumn = new TableColumn<DataModel, Integer>("Tel. Number");
telNumberColumn.setMinWidth(100);
//dodajemy kolumny do okna
tableView.getColumns().addAll(nameColumn,surnameColumn,ageColumn,telNumberColumn);
//podajemy nazwy zmiennych, których wartości mają się wyświetlać w poszczególnych kolumnach
nameColumn.setCellValueFactory(new PropertyValueFactory<>("sName"));
surnameColumn.setCellValueFactory(new PropertyValueFactory<>("sSurname"));
ageColumn.setCellValueFactory(new PropertyValueFactory<>("iAge"));
telNumberColumn.setCellValueFactory(new PropertyValueFactory<>("iPhoneNumber"));
//Sprawiamy że poszczególne kolumny stają się edytowalne
nameColumn.setCellFactory(TextFieldTableCell.<DataModel>forTableColumn());
nameColumn.setOnEditCommit((CellEditEvent<DataModel, String> event) -> {
((DataModel) event.getTableView().getItems(). get(event.getTablePosition().getRow())). setsName(event.getNewValue());
});
surnameColumn.setCellFactory(TextFieldTableCell.<DataModel>forTableColumn());
surnameColumn.setOnEditCommit((CellEditEvent<DataModel, String> event) -> {
((DataModel) event.getTableView().getItems(). get(event.getTablePosition().getRow())). setsSurname(event.getNewValue());
});
X ageColumn.setCellFactory(TextFieldTableCell.<DataModel>forTableColumn());
XX ageColumn.setOnEditCommit((CellEditEvent<DataModel, Integer> event) -> {
// ((DataModel) event.getTableView().getItems(). get(event.getTablePosition().getRow())). setiAge(Integer.valueOf(event.getNewValue()));
});
X telNumberColumn.setCellFactory(TextFieldTableCell.<DataModel>forTableColumn());
XX telNumberColumn.setOnEditCommit((CellEditEvent<DataModel, Integer> event) -> {
// ((DataModel) event.getTableView().getItems(). get(event.getTablePosition().getRow())). setiPhoneNumber(Integer.valueOf(event.getNewValue()));
});
tableView.setPlaceholder(new Label("Pust tabelka!"));//jaki element dodać jeśli tabelka nie jest wyświetlona
tableView.setEditable(true);
tableView.setItems(dataList); //wczytujemy dane do przygotowanej tabelki
buttAdd.setOnAction((ActionEvent e) -> {
buttAddAction(e);
});
}
I'm taking Oracle TableView tutorial, and it's quite difficult. Help.
Upvotes: 3
Views: 11129
Reputation: 41
I searched through a lot of answers and I've borrowed/extended/merged to this solution. Edits are committed when focus moves from edited cell. I have a public class for each datatype that can be represented in a table: EditingTextCell, EditingIntegerCell etc. These public classes can be applied to any table provided that the data is represented as an observable list of a class that accesses the data to be displayed as properties. I publish this solution because I was faced with creating a class for each column of each table in my application. Currently, the double value and combobox cell versions are tied to specific columns of specific tables. I'll do a generalized version of these as time permits. Please forgive my not presenting the source links -- I forgot to bookmark them as I perused them.
Java documentation suggests that easier ways of doing this are forthcoming.
Example usage for Integer field:
TableColumn<Factor, Number> noLevelsCol =
new TableColumn<>("No. Levels");
noLevelsCol.setCellValueFactory(
new PropertyValueFactory("numberLevels"));
noLevelsCol.setMinWidth(40);
noLevelsCol.setCellFactory(col -> new EditingIntegerCell<>());
noLevelsCol.setOnEditCommit((CellEditEvent<Factor, Number> t) -> {
((Factor) t.getTableView().getItems().get(
t.getTablePosition().getRow())
).setNumberLevels(t.getNewValue().intValue());
});
Example usage for String field:
TableColumn<Factor, String> nameCol = new TableColumn<>("Name");
nameCol.setMinWidth(60);
nameCol.setCellValueFactory(
new PropertyValueFactory("factorName"));
nameCol.setCellFactory(cellFactory);
nameCol.setOnEditCommit((CellEditEvent<Factor, String> t) -> {
((Factor) t.getTableView().getItems().get(
t.getTablePosition().getRow())
).setFactorName(t.getNewValue());
});
Definition of Factor class: public class Factor {
private final IntegerProperty factorID = new SimpleIntegerProperty();
public IntegerProperty getFactorID() { return factorID; }
private StringProperty factorName = new SimpleStringProperty();
public void setFactorName(String value) {
factorNameProperty().set(value); }
public String getFactorName() { return factorNameProperty().get(); }
public StringProperty factorNameProperty() {
if (factorName == null) factorName =
new SimpleStringProperty(this, "factorName");
return factorName;
}
private IntegerProperty numberLevels = new SimpleIntegerProperty();
public void setNumberLevels(int value) {
numberLevelsProperty().set(value); }
public IntegerProperty getNumberLevels() { return numberLevels; }
public IntegerProperty numberLevelsProperty() {
if (numberLevels == null) numberLevels =
new SimpleIntegerProperty(this, "numberLevels");
return numberLevels;
}
private StringProperty listOfLevels = new SimpleStringProperty();
public void setListOfLevels(String value) {
listOfLevelsProperty().set(value); }
public String getListOfLevels() { return listOfLevelsProperty().get(); }
public StringProperty listOfLevelsProperty() {
if (listOfLevels == null) listOfLevels =
new SimpleStringProperty(this, "listOfLevels");
return listOfLevels;
}
// Constructors
public Factor(int factorID, String factorName) {
this.factorID.set(factorID);
this.factorName.set(factorName);
this.numberLevels.set(1);
this.listOfLevels.set("-1, 1");
}
public Factor(int factorID, String factorName, int numberLevels,
String listOfLevels) {
this.factorID.set(factorID);
this.factorName.set(factorName);
this.numberLevels.set(numberLevels);
this.listOfLevels.set(listOfLevels);
}
@Override
public String toString() {
return "Factor{" + "factorName=" + factorName + '}';
}
public String[] getLevels() {
return listOfLevels.getValue().split(",");
}
}
Loading the data into the table final ObservableList factorList = FXCollections.observableArrayList( new Factor(1, "Factor1", 2, "-1, 1") );
factorTableView.setEditable(true);
factorTableView.getColumns().clear();
factorTableView.setItems(factorList);
boolean addAll;
addAll = factorTableView.getColumns().addAll(idCol,
nameCol, noLevelsCol, levelsCol);
The EditingIntegerCell class public class EditingIntegerCell extends TableCell {
private TextField textField;
private final Pattern intPattern = Pattern.compile("-?\\d+");
public EditingIntegerCell() {
}
@Override
public void startEdit() {
if (!isEmpty()) {
super.startEdit();
createTextField();
setText(null);
setGraphic(textField);
textField.selectAll();
}
}
@Override
public void cancelEdit() {
super.cancelEdit();
setText((String) getItem().toString());
setGraphic(null);
}
@Override
public void updateItem(Number 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(
(ObservableValue<? extends Boolean> arg0, Boolean arg1, Boolean arg2)
-> {
if (!arg2) {
processEdit();
}
});
}
private void processEdit() {
String text = textField.getText();
if (intPattern.matcher(text).matches()) {
commitEdit(Integer.parseInt(text));
} else {
cancelEdit();
}
}
private String getString() {
return getItem() == null ? "" : getItem().toString();
}
}
** The EditingTextCell class ** public class EditingTextCell extends TableCell {
private TextField textField;
public EditingTextCell() {
}
@Override
public void startEdit() {
if (!isEmpty()) {
super.startEdit();
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(
(ObservableValue<? extends Boolean> arg0, Boolean arg1, Boolean arg2)
-> {
if (!arg2) {
commitEdit(textField.getText());
}
});
}
private String getString() {
return getItem() == null ? "" : getItem();
}
}
Upvotes: 4
Reputation: 11134
The issue is that TextFieldTableCell.forTableColumn()
is typed to a String value. See the default implementation:
public static <S> Callback<TableColumn<S,String>, TableCell<S,String>> forTableColumn() {
return forTableColumn(new DefaultStringConverter());
}
What you need is the TextFieldTableCell
with an IntegerStringConverter
, for example:
ageColumn.setCellFactory(TextFieldTableCell.<DataModel, Integer>forTableColumn(new IntegerStringConverter()));
Upvotes: 8