I have a Tableview in Javafx in which I’m trying to add a row dynamically when user presses a button. The problem is that when doing it the previous data in the table gets duplicated. I must say that if I print the size of the items in the table, it returns the correct size, without the duplciates and that I can’t select the duplicated items nor scroll to see them all.
Here is my code:
detail.fxml
<TableView fx:id="tblPersonalExterno" prefHeight="200.0" prefWidth="200.0" VBox.vgrow="ALWAYS">
<columns>
<TableColumn prefWidth="781.0000244379044">
<graphic>
<Label text="Externo" />
</graphic>
<columns>
<TableColumn fx:id="tblcNombreExterno" prefWidth="270" text="Nombre"/>
<TableColumn fx:id="tblcEmpresaExterno" prefWidth="270" text="Empresa" />
<TableColumn fx:id="tblcInfPRLExterno" minWidth="0.0" prefWidth="59.0">
<graphic>
<Label text="Inf. PRL">
<tooltip>
<Tooltip text="Informado PRL" />
</tooltip>
</Label>
</graphic>
</TableColumn>
<TableColumn fx:id="tblcAprobadoPRLExterno" minWidth="0.0" prefWidth="73.00003051757812">
<graphic>
<Label text="Apr. Cl. PRL">
<tooltip>
<Tooltip text="Aprobado cliente PRL" />
</tooltip>
</Label>
</graphic>
</TableColumn>
<TableColumn fx:id="tblcFechaAprPRLExterno" prefWidth="100.0">
<graphic>
<Label text="Fecha apr.">
<tooltip>
<Tooltip text="Fecha de aprobación" />
</tooltip>
</Label>
</graphic>
</TableColumn>
<TableColumn prefWidth="60.0" text="Validez PRL">
<columns>
<TableColumn fx:id="tblcFechaDesdePRLExterno" prefWidth="100.0" text="Desde" />
<TableColumn fx:id="tblcFechaHastaPRLExterno" prefWidth="100.0" text="Hasta" />
</columns>
</TableColumn>
</columns>
</TableColumn>
</columns>
</TableView>
<ToolBar prefHeight="40.0" prefWidth="200.0">
<items>
<Button fx:id="btnAñadirExterno" mnemonicParsing="false" onAction="#addExterno">
<graphic>
<FontIcon iconLiteral="mdi-account-plus"
iconSize="16" />
</graphic>
<tooltip>
<Tooltip text="Añadir Personal Externo" />
</tooltip>
</Button>
<Button fx:id="btnEliminarExterno" mnemonicParsing="false">
<graphic>
<FontIcon iconLiteral="mdi-delete-forever"
iconSize="16" />
</graphic>
<tooltip>
<Tooltip text="Eliminar Personal Externo" />
</tooltip>
</Button>
</items>
</ToolBar>
DetailsController.java
public class DetailsController {
private ObservableList<Responsable> listaPersonalExterno = FXCollections.observableArrayList();
@FXML
private TableView<Responsable> tblPersonalExterno;
@FXML
private TableColumn<Responsable, String> tblcNombreExterno;
@FXML
private TableColumn<Responsable, String> tblcEmpresaExterno;
@FXML
private TableColumn<Responsable, Boolean> tblcInfPRLExterno;
@FXML
private TableColumn<Responsable, Boolean> tblcAprobadoPRLExterno;
@FXML
private TableColumn<Responsable, LocalDate> tblcFechaAprPRLExterno;
@FXML
private TableColumn<Responsable, LocalDate> tblcFechaDesdePRLExterno;
@FXML
private TableColumn<Responsable, LocalDate> tblcFechaHastaPRLExterno;
public DetailsController() {
cargarPersonalExterno();
}
public ObservableList<Responsable> listaPersonalExterno(){
return listaPersonalExterno;
}
@FXML
public void initialize() {
doBindTablaPersonalExterno();
}
private void doBindTablaPersonalExterno() {
tblcNombreExterno.setCellValueFactory(new PropertyValueFactory<Responsable, String>("nombre"));
tblcNombreExterno.setCellFactory(col -> {
TableCell<Responsable, String> cell = new TableCell<Responsable, String>(){
@Override
public void updateItem(String item, boolean empty) {
super.updateItem(item, empty);
if(empty) {
setText("");
}else {
TextField tf = new TextField();
tf.setText(item);
setGraphic(tf);
}
}
};
return cell;
});
tblcEmpresaExterno.setCellValueFactory(new PropertyValueFactory<Responsable, String>("empresa"));
tblcEmpresaExterno.setCellFactory(col -> {
TableCell<Responsable, String> cell = new TableCell<Responsable, String>(){
@Override
public void updateItem(String item, boolean empty) {
super.updateItem(item, empty);
if(empty) {
setText("");
}else {
TextField tf = new TextField();
tf.setText(item);
setGraphic(tf);
}
}
};
return cell;
});
tblcInfPRLExterno.setCellValueFactory(cellData -> new SimpleBooleanProperty(cellData.getValue().getPrl().isInformado()));
tblcInfPRLExterno.setCellFactory(col -> {
TableCell<Responsable, Boolean> cell = new TableCell<Responsable, Boolean>(){
@Override
public void updateItem(Boolean item, boolean empty) {
super.updateItem(item, empty);
if(empty) {
setText("");
}else {
CheckBox chk = new CheckBox();
setStyle("-fx-alignment: CENTER;");
if(item != null) {
chk.setSelected(item);
}
setGraphic(chk);
}
}
};
return cell;
});
tblcAprobadoPRLExterno.setCellValueFactory(cellData -> new SimpleBooleanProperty(cellData.getValue().getPrl().isAprobado()));
tblcAprobadoPRLExterno.setCellFactory(col -> {
TableCell<Responsable, Boolean> cell = new TableCell<Responsable, Boolean>(){
@Override
public void updateItem(Boolean item, boolean empty) {
super.updateItem(item, empty);
if(empty) {
setText("");
}else {
CheckBox chk = new CheckBox();
setStyle("-fx-alignment: CENTER;");
if(item != null) {
chk.setSelected(item);
}
setGraphic(chk);
}
}
};
return cell;
});
tblcFechaAprPRLExterno.setCellValueFactory(cellData -> new SimpleObjectProperty<LocalDate>(cellData.getValue().getPrl().getFechaAprobacion()));
tblcFechaAprPRLExterno.setCellFactory(col -> {
TableCell<Responsable, LocalDate> cell = new TableCell<Responsable, LocalDate>(){
@Override
public void updateItem(LocalDate item, boolean empty) {
super.updateItem(item, empty);
if(empty) {
setText("");
}else {
DatePicker dp = new DatePicker();
setStyle("-fx-alignment: CENTER;");
dp.setValue(item);
setGraphic(dp);
}
}
};
return cell;
});
tblcFechaDesdePRLExterno.setCellValueFactory(cellData -> new SimpleObjectProperty<LocalDate>(cellData.getValue().getPrl().getValidez().getInicio()));
tblcFechaDesdePRLExterno.setCellFactory(col -> {
TableCell<Responsable, LocalDate> cell = new TableCell<Responsable, LocalDate>(){
@Override
public void updateItem(LocalDate item, boolean empty) {
super.updateItem(item, empty);
if(empty) {
setText("");
}else {
DatePicker dp = new DatePicker();
setStyle("-fx-alignment: CENTER;");
dp.setValue(item);
setGraphic(dp);
}
}
};
return cell;
});
tblcFechaHastaPRLExterno.setCellValueFactory(cellData -> new SimpleObjectProperty<LocalDate>(cellData.getValue().getPrl().getValidez().getInicio()));
tblcFechaHastaPRLExterno.setCellFactory(col -> {
TableCell<Responsable, LocalDate> cell = new TableCell<Responsable, LocalDate>(){
@Override
public void updateItem(LocalDate item, boolean empty) {
super.updateItem(item, empty);
if(empty) {
setText("");
}else {
DatePicker dp = new DatePicker();
setStyle("-fx-alignment: CENTER;");
dp.setValue(item);
setGraphic(dp);
}
}
};
return cell;
});
}
/**
* Carga el personal externo
*/
private void cargarPersonalExterno() {
List<Responsable> lista = new ArrayList<Responsable>();
Responsable r1 = new Responsable();
r1.setNombre("NEUS");
Responsable r2 = new Responsable();
r2.setNombre("PEPE");
lista.add(r1);
lista.add(r2);
listaPersonalExterno.addAll(lista);
}
@FXML
public void addExterno() {
Responsable r = new Responsable();
listaPersonalExterno.add(r);
}
}
Here is an image of the behaviour. The duplicated rows under the blank one seem to not exists because I can’t select them, can’t scroll and can’t access to them by code.
>Solution :
The problem with your code is when your item is empty or null, you are only setting the text to null but not the graphic. So the graphic part will still stay in the cell when the row/cell is reused.
Reset both the text and graphic when the item is empty in all your cell factories. And also it is a bad idea to create new nodes in the updateItem method. Create the DatePicker per cell instance and set it as graphic in the updateItem. Something like :
tblcFechaDesdePRLExterno.setCellFactory(col -> {
TableCell<Responsable, LocalDate> cell = new TableCell<Responsable, LocalDate>(){
private DatePicker dp = new DatePicker();
@Override
public void updateItem(LocalDate item, boolean empty) {
super.updateItem(item, empty);
if(empty) {
setText(null);
setGraphic(null); // THIS IS MISSING IN YOUR CODE
}else {
setStyle("-fx-alignment: CENTER;");
dp.setValue(item);
setGraphic(dp);
}
}
};
return cell;
});