I am implementing a custom editable text cell to get the TAB feature in tableview. All is working fine but when i delete a row, the editable cells below the deleted row all change the values at the same time. So typing in a cell below the deleted row will change the item for multiple cells in that column.
Screenshot 1 - Tableview populated with initial values & row to be deleted is selected
Screenshot 2 - Tableview after deleting selected row
Screenshot 3 - The editable text cells in the rows below the deleted rows are updated simultaneously
Incase I reduce the window size so that only 3 rows are visible at a time and hit the delete button, then the values of editable cell outside the current view get the behavior mentioned above.
Below is my code.
Main:
import javafx.application.Application;
import javafx.fxml.FXMLLoader;
import javafx.scene.Parent;
import javafx.scene.Scene;
import javafx.stage.Stage;
public class Main extends Application {
@Override
public void start(Stage primaryStage) throws Exception{
Parent root = FXMLLoader.load(getClass().getResource("IndexIssueSample.fxml"));
primaryStage.setTitle("Hello World");
primaryStage.setScene(new Scene(root, 600, 575));
primaryStage.show();
}
public static void main(String[] args) {
launch(args);
}
}
Controller:
import javafx.collections.FXCollections;
import javafx.collections.ObservableList;
import javafx.fxml.FXML;
import javafx.fxml.Initializable;
import javafx.scene.control.*;
import javafx.scene.control.cell.PropertyValueFactory;
import java.net.URL;
import java.util.ResourceBundle;
public class IndexIssueController implements Initializable {
@FXML
private TableView<Person> personTableView;
@FXML
private TableColumn<Person, String> someDataColumn;
@FXML
private TableColumn<Person, String> someMoreDataColumn;
@FXML
private TableColumn<Person, String> additionalDataColumn;
Person person = new Person();
@Override
public void initialize(URL location, ResourceBundle resources) {
someDataColumn.setCellValueFactory(new PropertyValueFactory<>("someData"));
someMoreDataColumn.setCellValueFactory(new PropertyValueFactory<>("someMoreData"));
ObservableList<Person> personObservableList = FXCollections.observableArrayList();
additionalDataColumn.setCellValueFactory(new PropertyValueFactory<>("additionalData"));
someDataColumn.setCellFactory(e -> new EditableTextCell(0));
someMoreDataColumn.setCellFactory(e -> new EditableTextCell(1));
Person initPerson = setUpPersonData();
Person personData1 = null, personData2 = null, personData3 = null, personData4 = null, personData5 = null, personData6 = null;
try {
personData1 = (Person) initPerson.clone();
personData2 = (Person) initPerson.clone();
personData3 = (Person) initPerson.clone();
personData4 = (Person) initPerson.clone();
personData5 = (Person) initPerson.clone();
personData6 = (Person) initPerson.clone();
personData1.setSomeData("1");
personData1.setSomeMoreData("a");
personData2.setSomeData("2");
personData2.setSomeMoreData("b");
personData3.setSomeData("3");
personData3.setSomeMoreData("c");
personData4.setSomeData("4");
personData4.setSomeMoreData("d");
personData5.setSomeData("5");
personData5.setSomeMoreData("e");
personData6.setSomeData("6");
personData6.setSomeMoreData("f");
} catch (CloneNotSupportedException e) {
e.printStackTrace();
}
personObservableList.addAll(personData1, personData2, personData3, personData4, personData5, personData6);
personTableView.setItems(personObservableList);
personTableView.getSelectionModel().setCellSelectionEnabled(true);
}
private Person setUpPersonData() {
try {
person.setSomeData("This is SomeData");
person.setSomeMoreData("This is SomeMoreDate");
person.setAdditionalData("This is AdditionalData");
} catch (Exception e1) {
e1.printStackTrace();
}
return person;
}
@FXML
public void deleteRowButtonPushed(){
personTableView.getItems().remove(personTableView.getSelectionModel().getSelectedItem());
}
}
Custom Editable text cell:
import javafx.collections.ObservableList;
import javafx.scene.control.*;
class EditableTextCell extends TableCell<Person, String> {
private final TextField textField;
EditableTextCell(int columnIndex) {
textField = new TextField();
this.indexProperty().addListener((obs, oldValue, newValue) -> {
ObservableList<Person> tableData = getTableView().getItems();
int oldIndex = oldValue.intValue();
if (oldIndex >= 0 && oldIndex < tableData.size()) {
if (columnIndex == 0)
textField.textProperty().unbindBidirectional(tableData.get(oldIndex).someDataProperty());
if (columnIndex == 1)
textField.textProperty().unbindBidirectional(tableData.get(oldIndex).someMoreDataProperty());
}
int newIndex = newValue.intValue();
if (newIndex >= 0 && newIndex < tableData.size()) {
if(columnIndex == 0){
textField.textProperty().bindBidirectional(tableData.get(newIndex).someDataProperty());
//System.out.println("Printing value for newIndex " + newIndex + " someData textField " + textField.getText() + " someDataProperty " + tableData.get(newIndex).someDataProperty().getValue());
}
if(columnIndex == 1){
textField.textProperty().bindBidirectional(tableData.get(newIndex).someMoreDataProperty());
//System.out.println("Printing value for newIndex " + newIndex + " someMoreData textField " + textField.getText()+ " someMoreDataProperty " + tableData.get(newIndex).someMoreDataProperty().getValue());
}
setGraphic(textField);
} else {
setGraphic(null);
}
});
}
}
Person Class:
import javafx.beans.property.SimpleStringProperty;
public class Person {
private SimpleStringProperty someData, someMoreData, additionalData;
public Person() {
this.someData = new SimpleStringProperty("");
this.someMoreData = new SimpleStringProperty("");
this.additionalData = new SimpleStringProperty("");
}
public Person(String someData, String someMoreData, String additionalData) {
this.someData = new SimpleStringProperty(someData);
this.someMoreData = new SimpleStringProperty(someMoreData);
this.additionalData = new SimpleStringProperty(additionalData);
}
@Override
public Object clone()throws CloneNotSupportedException{
String someDataCloned = this.someData.getValue();
String someMoreDataCloned = this.someMoreData.getValue();
String additionalDataCloned = this.additionalData.getValue();
Person personCloned = new Person(someDataCloned, someMoreDataCloned, additionalDataCloned);
return personCloned;
}
public String getSomeData() {
return someData.get();
}
public SimpleStringProperty someDataProperty() {
return someData;
}
public void setSomeData(String someData) {
this.someData.set(someData);
}
public String getSomeMoreData() {
return someMoreData.get();
}
public SimpleStringProperty someMoreDataProperty() {
return someMoreData;
}
public void setSomeMoreData(String someMoreData) {
this.someMoreData.set(someMoreData);
}
public String getAdditionalData() {
return additionalData.get();
}
public SimpleStringProperty additionalDataProperty() {
return additionalData;
}
public void setAdditionalData(String additionalData) {
this.additionalData.set(additionalData);
}
}
FXML:
<?xml version="1.0" encoding="UTF-8"?>
<?import com.jfoenix.controls.JFXButton?>
<?import javafx.scene.control.TableColumn?>
<?import javafx.scene.control.TableView?>
<?import javafx.scene.layout.AnchorPane?>
<?import javafx.scene.text.Font?>
<AnchorPane prefHeight="400.0" prefWidth="1250.0" xmlns="http://javafx.com/javafx/8.0.141" xmlns:fx="http://javafx.com/fxml/1" fx:controller="sample.IndexIssueController">
<children>
<TableView fx:id="personTableView" layoutY="50.0" AnchorPane.bottomAnchor="50.0" AnchorPane.leftAnchor="0.0" AnchorPane.rightAnchor="0.0" AnchorPane.topAnchor="0.0">
<columns>
<TableColumn fx:id="someDataColumn" prefWidth="84.0" text="Some Data" />
<TableColumn fx:id="someMoreDataColumn" minWidth="100.0" prefWidth="100.0" text="Some More Data" />
<TableColumn fx:id="additionalDataColumn" minWidth="150.0" prefWidth="150.0" text="Additional Data" />
</columns>
</TableView>
<JFXButton fx:id="deleteRowButton" layoutX="14.0" layoutY="363.0" onAction="#deleteRowButtonPushed" style="-fx-background-color: #0097db;" text="Delete Row" textFill="WHITE" AnchorPane.bottomAnchor="10.0" AnchorPane.leftAnchor="10.0">
<font>
<Font name="Arial" size="12.0" />
</font>
</JFXButton>
</children>
</AnchorPane>