I have Tableview here that allows me to multiply the price and the quantity and put that into the subtotal column my question is i want to get the sum of all the subtotals for all the items in the table , thanks
private TableView<Product> table = new TableView<Product>();
private final ObservableList<Product> data =
FXCollections.observableArrayList(
new Product("Notebook", 10, 12),
new Product("Eraser", 20, 12),
new Product("Pencil", 30, 12),
new Product("Pen", 40, 12),
new Product("Glue", 50, 12));
final HBox hb = new HBox();
public static void main(String[] args) {
launch(args);
}
#Override
public void start(Stage stage) {
Scene scene = new Scene(new Group());
stage.setTitle("Book Store Sample");
stage.setWidth(650);
stage.setHeight(550);
final Label label = new Label("Book Store");
label.setFont(new Font("Arial", 20));
table.setEditable(true);
TableColumn name = new TableColumn("Name");
name.setMinWidth(100);
name.setCellValueFactory(
new PropertyValueFactory<Product, String>("name"));
name.setCellFactory(TextFieldTableCell.forTableColumn());
name.setOnEditCommit(
new EventHandler<CellEditEvent<Product, String>>() {
#Override
public void handle(CellEditEvent<Product, String> t) {
((Product) t.getTableView().getItems().get(
t.getTablePosition().getRow())
).setName(t.getNewValue());
}
}
);
TableColumn priceCol = new TableColumn("Price");
priceCol.setMinWidth(100);
priceCol.setCellValueFactory(
new PropertyValueFactory<Product, String>("price"));
priceCol.setCellFactory(TextFieldTableCell.<Product, Number>forTableColumn(new NumberStringConverter()));
priceCol.setOnEditCommit(
new EventHandler<CellEditEvent<Product, Number>>() {
#Override
public void handle(CellEditEvent<Product, Number> t) {
((Product) t.getTableView().getItems().get(
t.getTablePosition().getRow())
).setPrice(t.getNewValue().intValue());
}
}
);
TableColumn quantityCol = new TableColumn("Quantity");
quantityCol.setMinWidth(200);
quantityCol.setCellValueFactory(
new PropertyValueFactory<Product, Number>("quantity"));
quantityCol.setCellFactory(TextFieldTableCell.<Product, Number>forTableColumn(new NumberStringConverter()));
quantityCol.setOnEditCommit(
new EventHandler<CellEditEvent<Product, Number>>() {
#Override
public void handle(CellEditEvent<Product, Number> t) {
((Product) t.getTableView().getItems().get(
t.getTablePosition().getRow())
).setQuantity(t.getNewValue().intValue());
}
}
);
TableColumn subTotalCol = new TableColumn("Sub Total");
subTotalCol.setMinWidth(200);
subTotalCol.setCellValueFactory(
new PropertyValueFactory<Product, String>("subTotal"));
table.setItems(data);
table.getColumns().addAll(name, priceCol, quantityCol, subTotalCol);
final TextField addName = new TextField();
addName.setPromptText("Name");
addName.setMaxWidth(name.getPrefWidth());
final TextField addPrice = new TextField();
addPrice.setMaxWidth(priceCol.getPrefWidth());
addPrice.setPromptText("Price");
final TextField addQuantity = new TextField();
addQuantity.setMaxWidth(quantityCol.getPrefWidth());
addQuantity.setPromptText("Quantity");
final Button addButton = new Button("Add");
addButton.setOnAction(new EventHandler<ActionEvent>() {
#Override
public void handle(ActionEvent e) {
data.add(new Product(
name.getText(),
Integer.parseInt(addPrice.getText()),
Integer.parseInt(addQuantity.getText())));
addName.clear();
addPrice.clear();
addQuantity.clear();
}
});
hb.getChildren().addAll(addName, addPrice, addQuantity, addButton);
hb.setSpacing(3);
final VBox vbox = new VBox();
vbox.setSpacing(5);
vbox.setPadding(new Insets(10, 0, 0, 10));
vbox.getChildren().addAll(label, table, hb);
((Group) scene.getRoot()).getChildren().addAll(vbox);
stage.setScene(scene);
stage.show();
}
public static class Product {
private final SimpleStringProperty name;
private final SimpleIntegerProperty price;
private final SimpleIntegerProperty quantity;
private final SimpleIntegerProperty subTotal;
private Product(String name, int price, int quantity) {
this.name = new SimpleStringProperty(name);
this.price = new SimpleIntegerProperty(price);
this.quantity = new SimpleIntegerProperty(quantity);
this.subTotal = new SimpleIntegerProperty();
NumberBinding multiplication = Bindings.multiply(this.priceProperty(), this.quantityProperty());
this.subTotalProperty().bind(multiplication);
}
public String getName() {
return name.get();
}
public SimpleStringProperty nameProperty() {
return name;
}
public void setName(String name) {
this.name.set(name);
}
public int getPrice() {
return price.get();
}
public SimpleIntegerProperty priceProperty() {
return price;
}
public void setPrice(int price) {
this.price.set(price);
}
public int getQuantity() {
return quantity.get();
}
public SimpleIntegerProperty quantityProperty() {
return quantity;
}
public void setQuantity(int quantity) {
this.quantity.set(quantity);
}
public int getSubTotal() {
return subTotal.get();
}
public SimpleIntegerProperty subTotalProperty() {
return subTotal;
}
public void setSubTotal(int subTotal) {
this.subTotal.set(subTotal);
}
}
}
You can create a binding that tracks the total of all the subtotal values:
IntegerBinding total = Bindings.createIntegerBinding(() ->
table.getItems().stream().collect(Collectors.summingInt(Product::getSubTotal)),
table.getItems());
The first parameter to this is a function that computes the total of the results of calling product.getSubTotal() on each element in table.getItems(). The second argument ensures that this binding is marked as invalid (so can be recomputed) any time table.getItems() is invalidated.
Using the default mechanism of constructing a list, as you do with ObservableList<Product> data = FXCollections.observableArrayList(...), the list will only be invalidated when items are added, removed, or replaced, but not if the subTotal of an existing item changes. To make this happen, you need to modify that line to use a extractor:
private final ObservableList<Product> data = FXCollections.observableList(Arrays.asList(
new Product("Notebook", 10, 12),
new Product("Eraser", 20, 12),
new Product("Pencil", 30, 12),
new Product("Pen", 40, 12),
new Product("Glue", 50, 12)),
product -> new Observable[] {product.subTotalProperty()});
Now you can do something like
Label totalLabel = new Label();
totalLabel.textProperty().bind(Bindings.format("Total: %d", total));
Update
Here is what the IntegerBinding code looks like without a lambda expression, though it is far less clear and I don't recommend doing this:
IntegerBinding total = Bindings.createIntegerBinding(new Callable<Integer>() {
#Override
public Integer call() {
return table.getItems().stream().collect(Collectors.summingInt(
new ToIntFunction<Product>() {
#Override
public int applyAsInt(Product product) {
return product.getSubTotal();
}
}));
}
}, table.getItems());
Related
I'm Actually new with JavaFX and been facing an issue to view the data on the table.
What I'm trying to do is to create a new stage on a Mouse Click Event of an Imageview where the Fields will be provided to the user to enter the data to the table on the parent node.
This is the function which is called on the mouse click event that creates a child node(stage) :
private void addQualification(MouseEvent event) {
System.out.println("Add Qualification Method");
final Stage dialog = new Stage();
dialog.setTitle("Add a Qualification");
dialog.initModality(Modality.WINDOW_MODAL);
dialog.initStyle(StageStyle.UTILITY);
// create a grid for the data entry.
GridPane grid = new GridPane();
AnchorPane anchorPane = new AnchorPane(grid);
ObservableList<String> QualChoiceList = FXCollections.observableArrayList("MBBS/MSC ", "MD/MS/DNB/PhD", "DM/M Ch.");
final ChoiceBox<String> QualChoice = new ChoiceBox<String>();
QualChoice.setValue("Select a Qualification");
QualChoice.setItems(QualChoiceList);
final TextField College = new TextField();
final TextField University = new TextField();
final TextField PassingYear = new TextField();
final TextField RegNo = new TextField();
final TextField StateName = new TextField();
grid.addRow(0, new Label("Qualification"), QualChoice);
grid.addRow(1, new Label("College"), College);
grid.addRow(2, new Label("University"), University);
grid.addRow(3, new Label("Year of Passing"), PassingYear);
grid.addRow(4, new Label("Registration No."), RegNo);
grid.addRow(5, new Label("Name of State"), StateName);
grid.setHgap(10);
grid.setVgap(10);
GridPane.setHgrow(QualChoice, Priority.ALWAYS);
GridPane.setHgrow(College, Priority.ALWAYS);
GridPane.setHgrow(University, Priority.ALWAYS);
GridPane.setHgrow(PassingYear, Priority.ALWAYS);
GridPane.setHgrow(RegNo, Priority.ALWAYS);
GridPane.setHgrow(StateName, Priority.ALWAYS);
// create action buttons for the dialog.
Button ok = new Button("Add");
Button cancel = new Button("Cancel");
ok.setDefaultButton(true);
cancel.setCancelButton(true);
anchorPane.getChildren().add(ok);
anchorPane.getChildren().add(cancel);
AnchorPane.setTopAnchor(grid, 20.0);
AnchorPane.setLeftAnchor(grid, 20.0);
AnchorPane.setRightAnchor(grid, 20.0);
AnchorPane.setBottomAnchor(grid, 80.0);
AnchorPane.setTopAnchor(ok, 240.0);
AnchorPane.setLeftAnchor(ok, 100.0);
AnchorPane.setRightAnchor(ok, 214.0);
AnchorPane.setBottomAnchor(ok, 30.0);
AnchorPane.setTopAnchor(cancel, 240.0);
AnchorPane.setLeftAnchor(cancel, 230.0);
AnchorPane.setRightAnchor(cancel, 95.0);
AnchorPane.setBottomAnchor(cancel, 30.0);
dialog.setScene(new Scene(anchorPane, 400, 310));
dialog.show();
// only enable the ok button when there has been some text entered.
ok.disableProperty().bind(College.textProperty().isEqualTo("").or(University.textProperty().isEqualTo("")).or(PassingYear.textProperty().isEqualTo("")).or(RegNo.textProperty().isEqualTo("")).or(StateName.textProperty().isEqualTo("")));
// add action handlers for the dialog buttons.
ok.setOnAction(new EventHandler<ActionEvent>() {
#Override
public void handle(ActionEvent actionEvent) {
int nextIndex = QualTable.getSelectionModel().getSelectedIndex() + 1;
QualTable.getItems().add(nextIndex, new Qualification(QualChoice.getValue(), College.getText(), University.getText(), PassingYear.getText(), RegNo.getText(), StateName.getText()));
QualTable.getSelectionModel().select(nextIndex);
dialog.close();
}
});
cancel.setOnAction(new EventHandler<ActionEvent>() {
#Override
public void handle(ActionEvent actionEvent) {
dialog.close();
}
});
This is the Qualification Class that I have used to inout the values to the QualTable using the add Method:
public static class Qualification {
private StringProperty Qual;
private StringProperty College;
private StringProperty University;
private StringProperty PassingYear;
private StringProperty RegNo;
private StringProperty StateName;
public Qualification(String Qual, String College, String University, String PassingYear, String RegNo, String StateName) {
setQual(Qual);
setCollege(College);
setUniversity(University);
setPassingYear(PassingYear);
setRegNo(RegNo);
setStateName(StateName);
}
public String getQual() {
return QualProperty().get();
}
public String getCollege() {
return CollegeProperty().get();
}
public String getUniversity() {
return UniversityProperty().get();
}
public String getPassingYear() {
return PassingYearProperty().get();
}
public String getRegNo() {
return RegNoProperty().get();
}
public String getStateName() {
return StateNameProperty().get();
}
public final void setQual(String value) {
QualProperty().set(value);
}
public final void setCollege(String Coll) {
CollegeProperty().set(Coll);
}
public final void setUniversity(String Univ) {
UniversityProperty().set(Univ);
}
public final void setPassingYear(String PsngYr) {
PassingYearProperty().set(PsngYr);
}
public final void setRegNo(String Reg) {
RegNoProperty().set(Reg);
}
public final void setStateName(String StNm) {
StateNameProperty().set(StNm);
}
public StringProperty QualProperty() {
if (Qual == null) {
Qual = new SimpleStringProperty(this, "Qual");
}
return Qual;
}
public StringProperty CollegeProperty() {
if (College == null) {
College = new SimpleStringProperty(this, "College");
}
return College;
}
public StringProperty UniversityProperty() {
if (University == null) {
University = new SimpleStringProperty(this, "University");
}
return University;
}
public StringProperty PassingYearProperty() {
if (PassingYear == null) {
PassingYear = new SimpleStringProperty(this, "PassingYear");
}
return PassingYear;
}
public StringProperty RegNoProperty() {
if (RegNo == null) {
RegNo = new SimpleStringProperty(this, "RegNo");
}
return RegNo;
}
public StringProperty StateNameProperty() {
if (StateName == null) {
StateName = new SimpleStringProperty(this, "StateName");
}
return StateName;
}
}
This is the FXML code of the Anchor Pane consisting the tableview QualTable
I Need help on this tableview and the function that I'm using to populate the table view...
The values are properly carried through the Qualification class but the tableview is not displaying the values on the row....
Please help me out through this and Thanks in Advance..!!!
javafx tableview does not show data from database
`public class packList` {
Stage primaryStage;
private TableView tbl = new TableView();
private TableColumn pSerial = new TableColumn<>("Serial No.");
private TableColumn pName = new TableColumn<>("Package Name");
private TableColumn<Packaged, String> pSpeed = new TableColumn<>("Speed");
private TableColumn<Packaged, String> pMonthlyFee = new TableColumn<>("Monthly Fee");
private TableColumn<Packaged, String> pConnection = new TableColumn<>("Connection Fee");
private TableColumn<Packaged, String> pOther = new TableColumn<>("Others");
private TableColumn<Packaged, String> pView = new TableColumn<>("View");
private TableColumn pEdit = new TableColumn<>("Edit");
private TableColumn pDelete = new TableColumn<>("Delete");
private Packaged packaged = new Packaged();
private PackageController packageController = new PackageController();
private ArrayList<Packaged> packagedList = new ArrayList<>();
private ObservableList<Packaged> ps;
public packList(Stage stage) {
this.primaryStage = stage;
this.primaryStage.show();
this.primaryStage.setScene(AllpackList());
}
#SuppressWarnings("unchecked")
public Scene AllpackLis() {
StackPane mainPane = new StackPane();
mainPane.setAlignment(Pos.TOP_LEFT);
StackPane pane = new StackPane();
pane.setAlignment(Pos.TOP_LEFT);
// empName.prefWidthProperty().bind(tbl.widthProperty().multiply(10));
packagedList = packageController.getAllPackage();
ps = FXCollections.observableArrayList(packagedList);
pSerial.setCellValueFactory(new PropertyValueFactory<>("pkg_id"));
pName.setCellValueFactory(new PropertyValueFactory<>("pkg_name"));
pSpeed.setCellValueFactory(new PropertyValueFactory<>("speed"));
pMonthlyFee.setCellValueFactory(new PropertyValueFactory<>("mFee"));
pView.setCellValueFactory(new PropertyValueFactory<>("view"));
pEdit.setCellValueFactory(new PropertyValueFactory<>("edit"));
pDelete.setCellValueFactory(new PropertyValueFactory<>("delete"));
tbl.setItems(ps);
StackPane.setMargin(tbl, new Insets(15, 15, 15, 15));
tbl.getColumns().addAll(pSerial, pName, pSpeed, pMonthlyFee, pView, pEdit, pDelete);
pane.getChildren().addAll(tbl);
// StackPane.setMargin(pane, new Insets(15, 15, 15, 15));
pane.getStyleClass().add("pane");
mainPane.getChildren().addAll(pane);
return Common.returnScene(mainPane);
}
}
and java class is :
public class 'Packaged' {
private int pkgId;
private String pkgName;
private String pkgSpeed;
private String pkgCFee;
private String pkgMFee;
public int getPkgId() {
return pkgId;
}
public void setPkgId(int pkgId) {
this.pkgId = pkgId;
}
public String getPkgName() {
return pkgName;
}
public void setPkgName(String pkgName) {
this.pkgName = pkgName;
}
public String getPkgSpeed() {
return pkgSpeed;
}
public void setPkgSpeed(String pkgSpeed) {
this.pkgSpeed = pkgSpeed;
}
public String getPkgCFee() {
return pkgCFee;
}
public void setPkgCFee(String pkgCFee) {
this.pkgCFee = pkgCFee;
}
public String getPkgMFee() {
return pkgMFee;
}
public void setPkgMFee(String pkgMFee) {
this.pkgMFee = pkgMFee;
}
}
Is there any way --by clicking on the embedded button -- to open a new scene that is related to the tableview raw selected :
for example :
public class TestClass extends Application {
private TableView<Person> table = new TableView<Person>();
private final ObservableList<Person> data =
FXCollections.observableArrayList(
new Person("A", "B", "email#example.com","info"),
new Person("X", "Y", "email2#example.com","info"),
new Person("Z", "W", "email2#example.com","info")
);
public static void main(String[] args) {
launch(args);
}
#SuppressWarnings({ "rawtypes", "unchecked" })
#Override
public void start(Stage stage) {
stage.setTitle("Table View Sample");
final Label label = new Label("Address Book");
label.setFont(new Font("Arial", 20));
final Label actionTaken = new Label();
table.setEditable(true);
TableColumn firstNameCol = new TableColumn("First Name");
firstNameCol.setMinWidth(100);
firstNameCol.setCellValueFactory(
new PropertyValueFactory<Person, String>("firstName"));
TableColumn lastNameCol = new TableColumn("Last Name");
lastNameCol.setMinWidth(100);
lastNameCol.setCellValueFactory(
new PropertyValueFactory<Person, String>("lastName"));
TableColumn emailCol = new TableColumn("Email");
emailCol.setMinWidth(200);
emailCol.setCellValueFactory(
new PropertyValueFactory<Person, String>("email"));
TableColumn<Person, Person> btnCol = new TableColumn<>("info");
btnCol.setMinWidth(150);
btnCol.setCellValueFactory(new Callback<CellDataFeatures<Person, Person>, ObservableValue<Person>>() {
#Override public ObservableValue<Person> call(CellDataFeatures<Person, Person> features) {
return new ReadOnlyObjectWrapper(features.getValue());
}
});
btnCol.setComparator(new Comparator<Person>() {
public int compare(Person p1, Person p2) {
return p1.getLikes().compareTo(p2.getLikes());
}
});
btnCol.setCellFactory(new Callback<TableColumn<Person, Person>, TableCell<Person, Person>>() {
#Override public TableCell<Person, Person> call(TableColumn<Person, Person> btnCol) {
Stage stage = null;
return new AddCell(stage, table);
}
});
table.setItems(data);
table.getColumns().addAll(firstNameCol, lastNameCol, emailCol, btnCol);
final VBox vbox = new VBox();
vbox.setSpacing(5);
vbox.setPadding(new Insets(10, 10, 10, 10));
vbox.getChildren().addAll(label, table, actionTaken);
VBox.setVgrow(table, Priority.ALWAYS);
stage.setScene(new Scene(vbox));
stage.show();
}
}
the Person Class :
public class Person {
private final SimpleStringProperty firstName;
private final SimpleStringProperty lastName;
private final SimpleStringProperty email;
private final SimpleStringProperty infos;
private Person(String fName, String lName, String email, String infos) {
this.firstName = new SimpleStringProperty(fName);
this.lastName = new SimpleStringProperty(lName);
this.email = new SimpleStringProperty(email);
this.infos = new SimpleStringProperty(infos);
}
public String getFirstName() {
return firstName.get();
}
public void setFirstName(String fName) {
firstName.set(fName);
}
public String getLastName() {
return lastName.get();
}
public void setLastName(String fName) {
lastName.set(fName);
}
public String getEmail() {
return email.get();
}
public void setEmail(String fName) {
email.set(fName);
}
public String getLikes() {
return infos.get();
}
public void setLikes(String likes) {
this.infos.set(likes);
}
}
}
the AddCell class :
public class AddCell extends TableCell<Person, Boolean> {
final Button addButton = new Button("Elément");
final StackPane paddedButton = new StackPane();
final DoubleProperty buttonY = new SimpleDoubleProperty();
private MessageCmiService messageService=new MessageCmiServiceImpl();
public AddCell(final Stage stage, final TableView table) {
paddedButton.setPadding(new Insets(3));
paddedButton.getChildren().add(addButton);
addButton.setOnMousePressed(new EventHandler<MouseEvent>() {
#Override public void handle(MouseEvent mouseEvent) {
buttonY.set(mouseEvent.getScreenY());
}
});
addButton.setOnAction(new EventHandler<ActionEvent>() {
#Override public void handle(ActionEvent actionEvent) {
//???
// get the info
}
});
}
#Override
protected void updateItem(Boolean item, boolean empty) {
super.updateItem(item, empty);
if (empty) {
setGraphic(null);
}
else{
setContentDisplay(ContentDisplay.GRAPHIC_ONLY);
setGraphic(paddedButton);
}
}
}
In your handler, you can do
Person currentPerson = getTableView().getItems().get(getIndex());
which gives you access to all the data in the current row.
I have a TableView in JavaFX. It has a field subTotal which depends on the value of the fields quantity and price. I added a new column for the subTotal.
I have textfields present to add a new row to the table. But, the add button wants to have another textfield for the subTotal, although it does not really necessary for the subtotal column.
What I have tried so far :
TableColumn columnCodeProduct = new TableColumn("Product Code");
columnCodeProduct.setMinWidth(100);
columnCodeProduct.setCellValueFactory(new PropertyValueFactory<Data , Integer>("productname "));
TableColumn columnProductName = new TableColumn("Product Name");
columnProductName.setMinWidth(140);
columnProductName.setCellValueFactory(new PropertyValueFactory<Data , String>("codeproduct"));
TableColumn columnPrice = new TableColumn("Price");
columnPrice.setMinWidth(100);
columnPrice.setCellValueFactory(new PropertyValueFactory<Data , Integer>("price"));
TableColumn columQuantity = new TableColumn("Quantity");
columQuantity.setMinWidth(100);
columQuantity.setCellValueFactory(new PropertyValueFactory<Data , Integer>("quantity"));
TableColumn columnTotal = new TableColumn("Sub Total");
columnTotal.setMinWidth(100);
columQuantity.setCellValueFactory(new PropertyValueFactory<Data , Integer>("sub"));
tableData.getColumns().addAll(columnCodeProduct , columnProductName , columnPrice , columQuantity );
tableData.setItems(data);
addButton = new Button("Add Item");
addButton.setOnAction(new EventHandler<ActionEvent>() {
#Override
public void handle(ActionEvent event)
{
if(addproCodeTextfield.getText().isEmpty() || addproNameTextfield.getText().isEmpty()
|| addPriceTextfield.getText().isEmpty() || quantityTextField.getText().isEmpty())
{
System.out.println("Please Add information to all the fields");
} else {
data.add(new Data (
addproCodeTextfield.getText(),
addproNameTextfield.getText(),
addPriceTextfield.getText(),
quantityTextField.getText()));
methodTotal();
}
}
});
Data Class
public class Data
{
private final SimpleStringProperty codeproduct;
private final SimpleStringProperty productname;
private final SimpleStringProperty price ;
private final SimpleStringProperty quantity;
public Data (String code , String proname , String presyo , String quant )
{
this.codeproduct = new SimpleStringProperty(code);
this.productname = new SimpleStringProperty(proname);
this.price = new SimpleStringProperty(presyo);
this.quantity = new SimpleStringProperty(quant);
}
public String getcodeProduct()
{
return codeproduct.get();
}
public String getproductName()
{
return productname.get();
}
public String getPrice()
{
return price.get();
}
public String getQuantity()
{
return quantity.get();
}
}
I would restructure your model class as #ItachiUchiha suggests. If you feel you need to keep the data stored with String representations, you can just create a binding for the subtotal column:
TableColumn<Data, Number> subtotalColumn = new TableColumn<>("Sub Total");
subTotalColumn.setCellValueFactory(cellData -> {
Data data = cellData.getValue();
return Bindings.createDoubleBinding(
() -> {
try {
double price = Double.parseDouble(data.getPrice());
int quantity = Integer.parseInt(data.getQuantity());
return price * quantity ;
} catch (NumberFormatException nfe) {
return 0 ;
}
},
data.priceProperty(),
data.quantityProperty()
);
});
You can take benefit from JavaFX's power to bind value.
Few points to take care of while implementing a scenario as stated above:
The POJO class(in your case Data) fields must have correct types. For example price and quantity must be of SimpleIntegerProperty instead of SimpleStringProperty. This will help us in using Bindings.
SubTotal field depends on the values of price and quantity. The best way to achieve this is to bind subTotalProperty to a multiply Binding of price and quantity.
I have created a (not so) simple example basic editable tableview to show the approach. It has additional features, like editable cells, that you (or others seeking the same problem) might need ;)
import javafx.application.Application;
import javafx.beans.binding.Bindings;
import javafx.beans.binding.NumberBinding;
import javafx.beans.property.SimpleIntegerProperty;
import javafx.beans.property.SimpleStringProperty;
import javafx.collections.FXCollections;
import javafx.collections.ObservableList;
import javafx.event.ActionEvent;
import javafx.event.EventHandler;
import javafx.geometry.Insets;
import javafx.scene.Group;
import javafx.scene.Scene;
import javafx.scene.control.*;
import javafx.scene.control.TableColumn.CellEditEvent;
import javafx.scene.control.cell.PropertyValueFactory;
import javafx.scene.control.cell.TextFieldTableCell;
import javafx.scene.layout.HBox;
import javafx.scene.layout.VBox;
import javafx.scene.text.Font;
import javafx.stage.Stage;
import javafx.util.converter.NumberStringConverter;
public class TableViewSample extends Application {
private TableView<Product> table = new TableView<Product>();
private final ObservableList<Product> data =
FXCollections.observableArrayList(
new Product("Notebook", 10, 12),
new Product("Eraser", 20, 12),
new Product("Pencil", 30, 12),
new Product("Pen", 40, 12),
new Product("Glue", 50, 12));
final HBox hb = new HBox();
public static void main(String[] args) {
launch(args);
}
#Override
public void start(Stage stage) {
Scene scene = new Scene(new Group());
stage.setTitle("Book Store Sample");
stage.setWidth(650);
stage.setHeight(550);
final Label label = new Label("Book Store");
label.setFont(new Font("Arial", 20));
table.setEditable(true);
TableColumn name = new TableColumn("Name");
name.setMinWidth(100);
name.setCellValueFactory(
new PropertyValueFactory<Product, String>("name"));
name.setCellFactory(TextFieldTableCell.forTableColumn());
name.setOnEditCommit(
new EventHandler<CellEditEvent<Product, String>>() {
#Override
public void handle(CellEditEvent<Product, String> t) {
((Product) t.getTableView().getItems().get(
t.getTablePosition().getRow())
).setName(t.getNewValue());
}
}
);
TableColumn priceCol = new TableColumn("Price");
priceCol.setMinWidth(100);
priceCol.setCellValueFactory(
new PropertyValueFactory<Product, String>("price"));
priceCol.setCellFactory(TextFieldTableCell.<Product, Number>forTableColumn(new NumberStringConverter()));
priceCol.setOnEditCommit(
new EventHandler<CellEditEvent<Product, Number>>() {
#Override
public void handle(CellEditEvent<Product, Number> t) {
((Product) t.getTableView().getItems().get(
t.getTablePosition().getRow())
).setPrice(t.getNewValue().intValue());
}
}
);
TableColumn quantityCol = new TableColumn("Quantity");
quantityCol.setMinWidth(200);
quantityCol.setCellValueFactory(
new PropertyValueFactory<Product, Number>("quantity"));
quantityCol.setCellFactory(TextFieldTableCell.<Product, Number>forTableColumn(new NumberStringConverter()));
quantityCol.setOnEditCommit(
new EventHandler<CellEditEvent<Product, Number>>() {
#Override
public void handle(CellEditEvent<Product, Number> t) {
((Product) t.getTableView().getItems().get(
t.getTablePosition().getRow())
).setQuantity(t.getNewValue().intValue());
}
}
);
TableColumn subTotalCol = new TableColumn("Sub Total");
subTotalCol.setMinWidth(200);
subTotalCol.setCellValueFactory(
new PropertyValueFactory<Product, String>("subTotal"));
table.setItems(data);
table.getColumns().addAll(name, priceCol, quantityCol, subTotalCol);
final TextField addName = new TextField();
addName.setPromptText("Name");
addName.setMaxWidth(name.getPrefWidth());
final TextField addPrice = new TextField();
addPrice.setMaxWidth(priceCol.getPrefWidth());
addPrice.setPromptText("Price");
final TextField addQuantity = new TextField();
addQuantity.setMaxWidth(quantityCol.getPrefWidth());
addQuantity.setPromptText("Quantity");
final Button addButton = new Button("Add");
addButton.setOnAction(new EventHandler<ActionEvent>() {
#Override
public void handle(ActionEvent e) {
data.add(new Product(
name.getText(),
Integer.parseInt(addPrice.getText()),
Integer.parseInt(addQuantity.getText())));
addName.clear();
addPrice.clear();
addQuantity.clear();
}
});
hb.getChildren().addAll(addName, addPrice, addQuantity, addButton);
hb.setSpacing(3);
final VBox vbox = new VBox();
vbox.setSpacing(5);
vbox.setPadding(new Insets(10, 0, 0, 10));
vbox.getChildren().addAll(label, table, hb);
((Group) scene.getRoot()).getChildren().addAll(vbox);
stage.setScene(scene);
stage.show();
}
public static class Product {
private final SimpleStringProperty name;
private final SimpleIntegerProperty price;
private final SimpleIntegerProperty quantity;
private final SimpleIntegerProperty subTotal;
private Product(String name, int price, int quantity) {
this.name = new SimpleStringProperty(name);
this.price = new SimpleIntegerProperty(price);
this.quantity = new SimpleIntegerProperty(quantity);
this.subTotal = new SimpleIntegerProperty();
NumberBinding multiplication = Bindings.multiply(this.priceProperty(), this.quantityProperty());
this.subTotalProperty().bind(multiplication);
}
public String getName() {
return name.get();
}
public SimpleStringProperty nameProperty() {
return name;
}
public void setName(String name) {
this.name.set(name);
}
public int getPrice() {
return price.get();
}
public SimpleIntegerProperty priceProperty() {
return price;
}
public void setPrice(int price) {
this.price.set(price);
}
public int getQuantity() {
return quantity.get();
}
public SimpleIntegerProperty quantityProperty() {
return quantity;
}
public void setQuantity(int quantity) {
this.quantity.set(quantity);
}
public int getSubTotal() {
return subTotal.get();
}
public SimpleIntegerProperty subTotalProperty() {
return subTotal;
}
public void setSubTotal(int subTotal) {
this.subTotal.set(subTotal);
}
}
}
Screenshot
Note - I have defined setCellFactory and setOnCommit to each of the columns. This is because the name, price and quantity columns are editable. You are most welcome to remove them in case you do not seek editable property.
I'm trying to build a billing system in javafx, and I could build the table and everything. Now I want to change it, I want the table to be already editable, i.e the text fields in the table should be enabled to edit before onEditCommite. Also storing the data in the table is also giving a problem.
The code is given below. In the below code, the rows are being added and can be deleted. But I want to make it editable when the "new bill" button in clicked. Also any method to calculate the Price by multiplying the this.rate and this.qty.
public class abc extends Application {
private TableView<Billing> table = new TableView<Billing>();
private final ObservableList<Billing> data = FXCollections.observableArrayList();
final HBox hb = new HBox();
final HBox hb1=new HBox();
final HBox hb2= new HBox();
private IntegerProperty index = new SimpleIntegerProperty();
public static void main(String[] args) {
launch(args); }
// Stage Start method. Whole Stage.
#Override
public void start(Stage stage) {
Scene scene = new Scene(new Group());
stage.setTitle("Billing");
stage.setWidth(550);
stage.setHeight(650);
final Label label = new Label("Billing Trial 2 ");
label.setFont(new Font("Comic Sans", 26));
//Call EditingCell and set true to make it editable
table.setEditable(true);
Callback<TableColumn, TableCell> cellFactory =
new Callback<TableColumn, TableCell>() {
public TableCell call(TableColumn p) {
return new EditingCell();
}
};
TableColumn srnoCol = new TableColumn("Sr. No. ");
srnoCol.setMinWidth(50);
srnoCol.setCellValueFactory(
new PropertyValueFactory<Billing, String>("srNo"));
srnoCol.setCellFactory(cellFactory);
TableColumn numCol = new TableColumn("Item Code ");
numCol.setMinWidth(50);
numCol.setCellValueFactory(
new PropertyValueFactory<Billing, String>("itemCode"));
numCol.setCellFactory(cellFactory);
numCol.setOnEditCommit(
new EventHandler<CellEditEvent<Billing, String>>() {
#Override
public void handle(CellEditEvent<Billing, String> t) {
((Billing) t.getTableView().getItems().get(
t.getTablePosition().getRow())
).setItemCode(t.getNewValue());
}
}
);
TableColumn nameCol = new TableColumn("Item Name ");
nameCol.setMinWidth(100);
nameCol.setCellValueFactory(
new PropertyValueFactory<Billing, String>("itemName"));
nameCol.setCellFactory(cellFactory);
nameCol.setOnEditCommit(
new EventHandler<CellEditEvent<Billing, String>>() {
#Override
public void handle(CellEditEvent<Billing, String> t) {
((Billing) t.getTableView().getItems().get(
t.getTablePosition().getRow())
).setItemName(t.getNewValue());
}
}
);
TableColumn qtyCol = new TableColumn("Qty ");
qtyCol.setMinWidth(100);
qtyCol.setCellValueFactory(
new PropertyValueFactory<Billing, String>("itemQty"));
qtyCol.setCellFactory(cellFactory);
qtyCol.setOnEditCommit(
new EventHandler<CellEditEvent<Billing, String>>() {
#Override
public void handle(CellEditEvent<Billing, String> t) {
((Billing) t.getTableView().getItems().get(
t.getTablePosition().getRow())
).setItemQty(t.getNewValue());
}
}
);
TableColumn rateCol = new TableColumn("Item Rate ");
rateCol.setMinWidth(50);
rateCol.setCellValueFactory(
new PropertyValueFactory<Billing, String>("itemRate"));
rateCol.setCellFactory(cellFactory);
rateCol.setOnEditCommit(
new EventHandler<CellEditEvent<Billing, String>>() {
#Override
public void handle(CellEditEvent<Billing, String> t) {
((Billing) t.getTableView().getItems().get(
t.getTablePosition().getRow())
).setItemRate(t.getNewValue());
}
}
);
TableColumn priceCol = new TableColumn("Item Price ");
priceCol.setMinWidth(50);
priceCol.setCellValueFactory(
new PropertyValueFactory<Billing, String>("itemPrice"));
priceCol.setCellFactory(cellFactory);
priceCol.setOnEditCommit(
new EventHandler<CellEditEvent<Billing, String>>() {
#Override
public void handle(CellEditEvent<Billing, String> t) {
((Billing) t.getTableView().getItems().get(
t.getTablePosition().getRow())
).setItemPrice(t.getNewValue());
}
}
);
table.setItems(data);
//indexing of elements for deleting function.
table.getSelectionModel().selectedItemProperty().addListener(newChangeListener<Object>() {
#Override
public void changed(ObservableValue<?>observable, Object oldvalue, Object newValue){
index.set(data.indexOf(newValue));
System.out.println("index: "+data.indexOf(newValue));
}
});
//Deleting
final Button deleteButton=new Button("Delete");
deleteButton.setOnAction(new EventHandler<ActionEvent>() {
#Override
public void handle(ActionEvent de){
int i = index.get();
if (i>-1){
data.remove(i);
table.getSelectionModel().clearSelection();
}
}
});
TableColumn amount = new TableColumn("Amount");
amount.getColumns().addAll(rateCol, priceCol);
table.setItems(data);
table.getColumns().addAll(srnoCol, numCol, nameCol, qtyCol, amount );
//add bill
final Button addButton = new Button("New Bill");
addButton.setOnAction(new EventHandler<ActionEvent>(){
#Override
public void handle(ActionEvent ae)
{
EditingCell ec = new EditingCell();
ec.getString();
ec.createTextField();
table.setEditable(true);
Callback<TableColumn, TableCell> cellFactory =
new Callback<TableColumn, TableCell>() {
public TableCell call(TableColumn p) {
return new EditingCell();
}
};
ec.startEdit();
ec.cancelEdit();
data.add(new Billing(null,null,null,null,null,null));
}
});
hb.getChildren().addAll( addButton, deleteButton);
hb.setAlignment(Pos.BASELINE_LEFT);
hb.setSpacing(16);
final Label label2 = new Label();
label2.setAlignment(Pos.BASELINE_RIGHT);
final VBox vbox = new VBox();
vbox.setSpacing(15);
vbox.setPadding(new Insets(10, 0, 0, 10));
vbox.getChildren().addAll(label,hb2, hb, table,hb1);
((Group) scene.getRoot()).getChildren().addAll(vbox);
stage.setScene(scene);
stage.show();
}
//Class Billing
public static class Billing {
private final SimpleStringProperty itemSrNo;
private final SimpleStringProperty itemCode;
private final SimpleStringProperty itemName;
private final SimpleStringProperty itemQty;
private final SimpleStringProperty itemRate;
private final SimpleStringProperty itemPrice;
private Billing(String iSrNo, String iCode, String iName, String iQty, String iRate,String iPrice)
{
this.itemSrNo = new SimpleStringProperty(iSrNo);
this.itemCode = new SimpleStringProperty(iCode);
this.itemName = new SimpleStringProperty(iName);
this.itemPrice = new SimpleStringProperty(iPrice);
this.itemQty = new SimpleStringProperty(iQty);
this.itemRate = new SimpleStringProperty(iRate);
}
public String getItemSrNo() {
return itemSrNo.get();
}
public void setItemSrNo(String iSrNo) {
itemSrNo.set(iSrNo);
}
public String getItemCode() {
return itemCode.get();
}
public void setItemCode(String iCode) {
itemCode.set(iCode);
}
public String getItemName() {
return itemName.get();
}
public void setItemName(String iName) {
itemName.set(iName);
}
public String getItemQty() {
return itemQty.get();
}
public void setItemQty(String iQty) {
itemQty.set(iQty);
}
public String getItemPrice() {
return itemPrice.get();
}
public void setItemPrice(String iPrice) {
itemPrice.set(iPrice);
}
public String getItemRate() {
return itemRate.get();
}
public void setItemRate(String iRate) {
itemRate.set(iRate);
}
}
//CellEditing
public class EditingCell extends TableCell<Billing, String> {
private TextField textField;
public EditingCell() {
}
#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(new ChangeListener<Boolean>(){
#Override
public void changed(ObservableValue<? extends Boolean> arg0,
Boolean arg1, Boolean arg2) {
if (!arg2) {
commitEdit(textField.getText());
}
}
});
}
private String getString() {
return getItem() == null ? "" : getItem().toString();
}
}
}
// calculation of Qty x Rate.
class Calculate {
public String sum(int iQty, int iRate)
{
int sum = iQty*iRate;
String s =""+sum;
return s;
}
}
Old question but here is what I think should work : Dont use setGraphic(null) in your cellFactory. Always set it to TextField. Also for the calculation part you can add listeners to your properties in the data model and perform calculation on any change of values for those properties.