BecauseTurtles BecauseTurtles - 8 days ago 5
Java Question

JavaFX GUI - Why does the Delete Button Function not work with the TableView?

Hello this is my first question as a newcomer to both Java and GUI design.
I have come across an odd situation that I feel I am just looking over.
I have TableView with some information, which can also be added with the Add Button. The Delete Button was working prior to me adding a FilterList constructor, so that I may filter search results by ID. Once I added the function, the Delete Button stopped working. I personally think that the deleteButtonClicked class isn't properly detecting the table anymore, because of the change by adding the Filter functionality. But as much as I try to keep both, one or the other usually stop working. I am currently trying to make it work without a Delete Button class, maybe by just making the product null.

The TableView functions are at the top of the code, while the delete button and functions are towards the bottom.

package application;

import javafx.application.Application;
import static javafx.application.Application.launch;
import javafx.collections.FXCollections;
import javafx.collections.ObservableList;
import javafx.collections.transformation.FilteredList;
import javafx.event.ActionEvent;
import javafx.event.EventHandler;
import javafx.geometry.Insets;
import javafx.geometry.Pos;
import javafx.scene.Scene;
import javafx.scene.control.Button;
import javafx.scene.control.Label;
import javafx.scene.control.TableColumn;
import javafx.scene.control.TableColumn.CellEditEvent;
import javafx.scene.control.TableView;
import javafx.scene.control.TextField;
import javafx.scene.layout.BorderPane;
import javafx.scene.layout.VBox;
import javafx.scene.text.Font;
import javafx.stage.Stage;
import javafx.scene.control.cell.PropertyValueFactory;
import javafx.scene.control.cell.TextFieldTableCell;
import javafx.scene.layout.HBox;
import javafx.scene.text.FontWeight;
import javafx.scene.text.Text;

public class MainApp extends Application {



TableView<Product> table = new TableView<>();


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

@Override
public void start(Stage primaryStage) throws Exception {
BorderPane root = new BorderPane();

BorderPane leftPane = new BorderPane();

FilteredList<Product> filterList = new FilteredList<>(productData);

table.setEditable(true);
//table.setItems(productData); // DELETE BUTTON WORKS WHEN THIS ONE IS ONLY SET
table.setItems(filterList); // DELETE BUTTON DOESN'T WORK WHEN
// THIS IS SET BUT, IS NEEDED TO FILTER SEARCH FUNCTIONS

TableColumn<Product, String> idNumberCol = new TableColumn<>("ID Number");
idNumberCol.setMinWidth(50);
idNumberCol.setCellValueFactory(new PropertyValueFactory<>("idNumber"));

idNumberCol.setCellFactory(TextFieldTableCell.<Product>forTableColumn());
idNumberCol.setOnEditCommit(
(CellEditEvent<Product, String> t) -> {
((Product) t.getTableView().getItems().get(
t.getTablePosition().getRow())
).setIdNumber(t.getNewValue());
});

TableColumn<Product, String> desCol = new TableColumn<>("Description");
desCol.setMinWidth(150);
desCol.setCellValueFactory(new PropertyValueFactory<>("description"));

desCol.setCellFactory(TextFieldTableCell.<Product>forTableColumn());
desCol.setOnEditCommit(
(CellEditEvent<Product, String> t) -> {
((Product) t.getTableView().getItems().get(
t.getTablePosition().getRow())
).setDescription(t.getNewValue());
});

TableColumn<Product, String> expCol = new TableColumn<>("Expiration");
expCol.setMinWidth(100);
expCol.setCellValueFactory(new PropertyValueFactory<>("expiration"));

expCol.setCellFactory(TextFieldTableCell.<Product>forTableColumn());
expCol.setOnEditCommit(
(CellEditEvent<Product, String> t) -> {
((Product) t.getTableView().getItems().get(
t.getTablePosition().getRow())
).setExpiration(t.getNewValue());
});


table.getColumns().addAll(idNumberCol, desCol, expCol);


VBox leftVbox = new VBox();
leftVbox.setPadding(new Insets(10, 20, 20, 20));
leftVbox.setSpacing(10);
leftVbox.getChildren().addAll(table);

BorderPane topPane = new BorderPane();

HBox topHbox = new HBox(); //Create HBox
topHbox.setPadding(new Insets(20, 20, 10, 20)); // Set padding and spacing for the hbox
topHbox.setSpacing(10); // The hbox will appropriately space out more

Text txtSearch = new Text("Search: ");
topHbox.getChildren().add(txtSearch);
txtSearch.setFont(Font.font("Tahoma", FontWeight.NORMAL, 16));

TextField txt = new TextField(""); // Create new TextField
txt.setPrefWidth(250);
txt.setPromptText("Search A Product ID"); // Sets up hint text inside the TextField
topHbox.getChildren().add(txt); // Add the TextField to the second hbox

HBox rightHbox = new HBox(); //Create HBox
rightHbox.setPadding(new Insets(20, 20, 10, 20)); // Set padding and spacing for the hbox
rightHbox.setSpacing(10); // The hbox will appropriately space out more

Text txtDetails = new Text("Product Details");
rightHbox.getChildren().add(txtDetails);
txtDetails.setFont(Font.font("Tahoma", FontWeight.NORMAL, 20));

txt.textProperty().addListener((obsVal, oldValue, newValue) -> {
filterList.setPredicate(product -> product.getIdNumber().contains(newValue));
});


BorderPane rightPane = new BorderPane();

VBox rightVbox = new VBox();
rightVbox.setPadding(new Insets(20, 10, 10, 20));
rightVbox.setSpacing(30);
rightVbox.setAlignment(Pos.TOP_LEFT);

Text txtId = new Text("ID: ");
rightVbox.getChildren().add(txtId);
txtId.setFont(Font.font("Tahoma", FontWeight.NORMAL, 16));

Text txtDes = new Text("Description: ");
rightVbox.getChildren().add(txtDes);
txtDes.setFont(Font.font("Tahoma", FontWeight.NORMAL, 16));

Text txtExp = new Text("Expiration: ");
rightVbox.getChildren().add(txtExp);
txtExp.setFont(Font.font("Tahoma", FontWeight.NORMAL, 16));

Text txtStatus = new Text("Status: ");
rightVbox.getChildren().add(txtStatus);
txtStatus.setFont(Font.font("Tahoma", FontWeight.NORMAL, 16));

Text txtEnter = new Text("Entered By: ");
rightVbox.getChildren().add(txtEnter);
txtEnter.setFont(Font.font("Tahoma", FontWeight.NORMAL, 16));

//EMPTY PLACE HOLDER INFORMATION TO POPULATE THE DATA
Label lblId;
Label lblDes;
Label lblExp;
Label lblStatus;
Label lblEnter;

HBox bottomHbox2 = new HBox();
bottomHbox2.setPadding(new Insets(20, 20, 20, 20));
bottomHbox2.setSpacing(10);
bottomHbox2.setAlignment(Pos.BOTTOM_RIGHT);


Button btnNew = new Button("Add...");
bottomHbox2.getChildren().add(btnNew);
btnNew.setOnAction(new EventHandler<ActionEvent>() {
@Override
public void handle(ActionEvent event) {

Stage secondStage = new Stage();

BorderPane dialogPane = new BorderPane();

VBox dialogVbox = new VBox();
dialogVbox.setPadding(new Insets(20, 10, 20, 20));
dialogVbox.setSpacing(20);

TextField addId = new TextField();
addId.setPromptText("ID Number");
addId.setMinWidth(50);
//addId.setMaxWidth(idNumberCol.getPrefWidth());

TextField addDes = new TextField();
addDes.setPromptText("Description");
addDes.setMinWidth(100);
//addDes.setMaxWidth(desCol.getPrefWidth());

TextField addExp = new TextField();
addExp.setPromptText("Expiration Date");
addExp.setMinWidth(50);
//addExp.setMaxWidth(expCol.getPrefWidth());

dialogVbox.getChildren().addAll(addId, addDes, addExp);

VBox dialogVbox2 = new VBox();
dialogVbox2.setPadding(new Insets(20, 20, 20, 10));
dialogVbox2.setSpacing(20);

Text txtIdLabel = new Text("ID: ");
dialogVbox2.getChildren().add(txtIdLabel);
txtIdLabel.setFont(Font.font("Tahoma", FontWeight.NORMAL, 16));

Text txtDesLabel = new Text("Description: ");
dialogVbox2.getChildren().add(txtDesLabel);
txtDesLabel.setFont(Font.font("Tahoma", FontWeight.NORMAL, 16));

Text txtExpLabel = new Text("Expiration: ");
dialogVbox2.getChildren().add(txtExpLabel);
txtExpLabel.setFont(Font.font("Tahoma", FontWeight.NORMAL, 16));

HBox addHbox = new HBox();
addHbox.setPadding(new Insets(20, 10, 50, 20));
addHbox.setSpacing(10);
addHbox.setAlignment(Pos.BOTTOM_RIGHT);

Button btnAdd = new Button("Add Product");
addHbox.getChildren().add(btnAdd);

btnAdd.setOnAction((ActionEvent e) -> {
productData.add(new Product(
addId.getText(),
addDes.getText(),
addExp.getText()));
addId.clear();
addDes.clear();
addExp.clear();
});

Button btnClose = new Button("Close");
addHbox.getChildren().add(btnClose);

btnClose.setOnAction(new EventHandler<ActionEvent>(){

@Override
public void handle(ActionEvent arg0) {
secondStage.close();
}

});

dialogPane.setCenter(dialogVbox);
dialogPane.setLeft(dialogVbox2);
dialogPane.setBottom(addHbox);

Scene scene2 = new Scene(dialogPane, 400, 200);

secondStage.setTitle("Add New Product");
secondStage.setScene(scene2);
secondStage.show();

//Set position of second window, related to primary window.
secondStage.setX(primaryStage.getX() + 250);
secondStage.setY(primaryStage.getY() + 100);

secondStage.show();
}
});

// DELETE BUTTON & EVENT HANDLER
Button btnDelete = new Button("Delete");
bottomHbox2.getChildren().add(btnDelete);

btnDelete.setOnAction(new EventHandler<ActionEvent>(){

@Override
public void handle(ActionEvent arg0) {
deleteButtonClicked();
}

});


Button btnSave = new Button("Save & Exit");
bottomHbox2.getChildren().add(btnSave);

btnSave.setOnAction(new EventHandler<ActionEvent>(){

@Override
public void handle(ActionEvent arg0) {
primaryStage.close();
}

});

rightVbox.getChildren().add(bottomHbox2);

topPane.setLeft(topHbox);
topPane.setCenter(rightHbox);
leftPane.setLeft(leftVbox);
rightPane.setLeft(rightVbox);
//rightPane.setCenter(bottomHbox2);

root.setTop(topPane);
root.setLeft(leftPane);
root.setCenter(rightPane);

Scene scene = new Scene(root, 650, 500);
primaryStage.setTitle("Perishables Application");
primaryStage.setScene(scene);
primaryStage.show();
}

// DELETE BUTTON FUNCTION
public void deleteButtonClicked() {
ObservableList<Product> productSelected, productData;
productData = table.getItems();
productSelected = table.getSelectionModel().getSelectedItems();

productSelected.forEach(productData::remove);
}

public ObservableList<Product> productData = FXCollections.observableArrayList(
new Product("001", "Cereal", "1/1/2017"),
new Product("002", "Dog Food", "3/3/2017"),
new Product("003", "Juice", "1/1/2018")
);


}


Any suggestions are greatly appreciated. Thanks for reading.

Answer

You have this error:

Exception in thread "JavaFX Application Thread" java.lang.UnsupportedOperationException at java.util.AbstractList.remove(AbstractList.java:161) at java.util.AbstractList$Itr.remove(AbstractList.java:374) at java.util.AbstractCollection.remove(AbstractCollection.java:293) at application.MainApp.deleteButtonClicked(MainApp.java:297) at application.MainApp$2.handle(MainApp.java:255) at application.MainApp$2.handle(MainApp.java:1)

Try replacing productSelected.forEach(productData::remove); with:

  productSelected.forEach(this.productData::remove);
                           |

Mention that you want to remove the data from the ObservableList not the FilteredList,when removing them from the ObservableList it will be automatically removed from the FilteredList<Product>


If you want to write you method with minimum code:

// DELETE BUTTON FUNCTION
  public void deleteButtonClicked() {
     table.getSelectionModel().getSelectedItems().forEach(this.productData::remove);
  }