I have a tableview and one of my columns consist of Integer values:
1
444
-9
If I sort on this column, I would expect 444 to be the "highest" number and therefor be the first row, but JavaFX believes 9 is higher?
Any tips?
MVCE:
public class TableViewTEST extends Application {
private TableView table = new TableView();
final ObservableList<Person> data = FXCollections.observableArrayList(
new Person("1", "Smith", "jacob.smith#example.com"),
new Person("9", "Johnson", "isabella.johnson#example.com"),
new Person("444", "Williams", "ethan.williams#example.com")
);
#Override
public void start(Stage stage) {
table.getItems().addAll(data);
Scene scene = new Scene(new Group());
stage.setTitle(
"Table View Sample");
stage.setWidth(
300);
stage.setHeight(
500);
final Label label = new Label("Address Book");
label.setFont(
new Font("Arial", 20));
TableColumn firstNameCol = new TableColumn("First");
TableColumn lastNameCol = new TableColumn("Last Name");
TableColumn emailCol = new TableColumn("Email");
firstNameCol.setCellValueFactory(
new PropertyValueFactory<Person, String>("firstName")
);
lastNameCol.setCellValueFactory(
new PropertyValueFactory<Person, String>("lastName")
);
emailCol.setCellValueFactory(
new PropertyValueFactory<Person, String>("email")
);
table.getColumns()
.addAll(firstNameCol, lastNameCol, emailCol);
final VBox vbox = new VBox();
vbox.setSpacing(
5);
vbox.setPadding(
new Insets(10, 0, 0, 10));
vbox.getChildren()
.addAll(label, table);
((Group) scene.getRoot()).getChildren().addAll(vbox);
stage.setScene(scene);
stage.show();
}
/**
* #param args the command line arguments
*/
public static void main(String[] args) {
launch(args);
}
}
Edit
Okay, I have found out the reason for this is because I use String in the model.. So the updated question is, how can you create a new sorting functionality on the table column so that it sorts as integer even though there's string in it?
Your values are Strings, so they are sorted lexicographically; since the character '4' comes before the character '9', '444' comes before '9'.
If you make those fields integer fields, then they will be sorted numerically. It's helpful here to use properly typed TableViews and TableColumns, instead of raw types.
import javafx.application.Application;
import javafx.beans.property.IntegerProperty;
import javafx.beans.property.SimpleIntegerProperty;
import javafx.beans.property.SimpleStringProperty;
import javafx.beans.property.StringProperty;
import javafx.collections.FXCollections;
import javafx.collections.ObservableList;
import javafx.geometry.Insets;
import javafx.scene.Group;
import javafx.scene.Scene;
import javafx.scene.control.Label;
import javafx.scene.control.TableColumn;
import javafx.scene.control.TableView;
import javafx.scene.control.cell.PropertyValueFactory;
import javafx.scene.layout.VBox;
import javafx.scene.text.Font;
import javafx.stage.Stage;
public class TableViewSortTest extends Application {
private TableView<Person> table = new TableView<>();
final ObservableList<Person> data = FXCollections.observableArrayList(
new Person(1, "Smith", "jacob.smith#example.com"),
new Person(9, "Johnson", "isabella.johnson#example.com"),
new Person(444, "Williams", "ethan.williams#example.com")
);
#Override
public void start(Stage stage) {
table.getItems().addAll(data);
Scene scene = new Scene(new Group());
stage.setTitle(
"Table View Sample");
stage.setWidth(
300);
stage.setHeight(
500);
final Label label = new Label("Address Book");
label.setFont(
new Font("Arial", 20));
TableColumn<Person, Integer> idCol = new TableColumn<>("Id");
TableColumn<Person, String> lastNameCol = new TableColumn<>("Last Name");
TableColumn<Person, String> emailCol = new TableColumn<>("Email");
idCol.setCellValueFactory(
new PropertyValueFactory<Person, Integer>("id")
);
lastNameCol.setCellValueFactory(
new PropertyValueFactory<Person, String>("name")
);
emailCol.setCellValueFactory(
new PropertyValueFactory<Person, String>("email")
);
table.getColumns()
.addAll(idCol, lastNameCol, emailCol);
final VBox vbox = new VBox();
vbox.setSpacing(
5);
vbox.setPadding(
new Insets(10, 0, 0, 10));
vbox.getChildren()
.addAll(label, table);
((Group) scene.getRoot()).getChildren().addAll(vbox);
stage.setScene(scene);
stage.show();
}
public static class Person {
private final IntegerProperty id = new SimpleIntegerProperty(this, "id");
private final StringProperty name = new SimpleStringProperty(this, "name");
private final StringProperty email = new SimpleStringProperty(this, "email");
public Person(int id, String name, String email) {
this.id.set(id);
this.name.set(name);
this.email.set(email);
}
public final IntegerProperty idProperty() {
return this.id;
}
public final int getId() {
return this.idProperty().get();
}
public final void setId(final int id) {
this.idProperty().set(id);
}
public final StringProperty nameProperty() {
return this.name;
}
public final java.lang.String getName() {
return this.nameProperty().get();
}
public final void setName(final java.lang.String name) {
this.nameProperty().set(name);
}
public final StringProperty emailProperty() {
return this.email;
}
public final java.lang.String getEmail() {
return this.emailProperty().get();
}
public final void setEmail(final java.lang.String email) {
this.emailProperty().set(email);
}
}
/**
* #param args the command line arguments
*/
public static void main(String[] args) {
launch(args);
}
}
You can create a Comparator:
firstNameCol.setComparator(new CustomComparator());
EDIT
Assuming you have a String type in your column, as it was originally posted, and you want to sort it numerically trying to parse all the strings, and in case there'are some non-integer strings, sort these lexicographically, this will be a valid Comparator:
private class CustomComparator implements Comparator<String>{
#Override
public int compare(String o1, String o2) {
if (o1 == null && o2 == null) return 0;
if (o1 == null) return -1;
if (o2 == null) return 1;
Integer i1=null;
try{ i1=Integer.valueOf(o1); } catch(NumberFormatException ignored){}
Integer i2=null;
try{ i2=Integer.valueOf(o2); } catch(NumberFormatException ignored){}
if(i1==null && i2==null) return o1.compareTo(o2);
if(i1==null) return -1;
if(i2==null) return 1;
return i1-i2;
}
}
Related
i can't get any further. I have a standart Tableview and inserted the part from James_D which gives me a colored row and a different colored selected cell
see here.
This works realy pretty good. Now i need to read the data from an internet site.
After inserting the code to read the data with InputStreamReader the colored row is not displayed any more at all. No other error. The read date is ok.
About any help or alternatives I would be very glad.
Thanks.
deleted the first "multi file version" of this problem.
css file: selected-row-table.css
.table-row-cell:selected-row {
-fx-background-color: lightskyblue ;
}
New all in one Error example based on James_D Tableview without .fxml file. Needed css file (see above) with name "selected-row-table.css".
Same error after "new InputStreamreader...".
package com.example.james1;
import javafx.application.Application;
import javafx.beans.binding.Bindings;
import javafx.beans.binding.BooleanBinding;
import javafx.beans.property.SimpleStringProperty;
import javafx.beans.property.StringProperty;
import javafx.collections.FXCollections;
import javafx.collections.ListChangeListener.Change;
import javafx.collections.ObservableList;
import javafx.collections.ObservableSet;
import javafx.css.PseudoClass;
import javafx.geometry.Insets;
import javafx.scene.Group;
import javafx.scene.Scene;
import javafx.scene.control.*;
import javafx.scene.control.cell.PropertyValueFactory;
import javafx.scene.layout.VBox;
import javafx.scene.text.Font;
import javafx.stage.Stage;
import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStreamReader;
import java.net.URL;
import java.net.URLConnection;
public class HelloApplication extends Application {
public static void main(String[] args) {
launch();
}
#Override
public void start(Stage stage) throws IOException {
Scene scene = new Scene(new Group());
scene.getStylesheets().add(getClass().getResource("selected-row-table.css").toExternalForm());
stage.setTitle("Table View Sample");
stage.setWidth(450);
stage.setHeight(600);
final Label label = new Label("Address Book");
label.setFont(new Font("Arial", 20));
final TableView<Person> table = new TableView<>();
final ObservableList<Person> data =
FXCollections.observableArrayList(
new Person("Jacob", "Smith", "jacob.smith#example.com"),
new Person("Isabella", "Johnson", "isabella.johnson#example.com"),
new Person("Ethan", "Williams", "ethan.williams#example.com"),
new Person("Emma", "Jones", "emma.jones#example.com"),
new Person("Michael", "Brown", "michael.brown#example.com")
);
table.getSelectionModel().setCellSelectionEnabled(true);
table.getSelectionModel().setSelectionMode(SelectionMode.MULTIPLE);
final PseudoClass selectedRowPseudoClass = PseudoClass.getPseudoClass("selected-row");
final ObservableSet<Integer> selectedRowIndexes = FXCollections.observableSet();
table.getSelectionModel().getSelectedCells().addListener((Change<? extends TablePosition> change) -> {
selectedRowIndexes.clear();
table.getSelectionModel().getSelectedCells().stream().map(TablePosition::getRow).forEach(row -> {
selectedRowIndexes.add(row);
});
});
table.setRowFactory(tableView -> {
final TableRow<Person> row = new TableRow<>();
BooleanBinding selectedRow = Bindings.createBooleanBinding(() ->
selectedRowIndexes.contains(new Integer(row.getIndex())), row.indexProperty(), selectedRowIndexes);
selectedRow.addListener((observable, oldValue, newValue) ->
row.pseudoClassStateChanged(selectedRowPseudoClass, newValue)
);
return row;
});
TableColumn<Person, String> firstNameCol = new TableColumn<>("First Name");
firstNameCol.setMinWidth(100);
firstNameCol.setCellValueFactory(new PropertyValueFactory<>("firstName"));
TableColumn<Person, String> lastNameCol = new TableColumn<>("Last Name");
lastNameCol.setMinWidth(100);
lastNameCol.setCellValueFactory(new PropertyValueFactory<>("lastName"));
TableColumn<Person, String> emailCol = new TableColumn<>("Email");
emailCol.setMinWidth(200);
emailCol.setCellValueFactory(new PropertyValueFactory<>("email"));
table.setItems(data);
table.getColumns().addAll(firstNameCol, lastNameCol, emailCol);
final VBox vbox = new VBox();
Button bt1 = new Button("add local");
Button bt2 = new Button("add internet");
vbox.setSpacing(5);
vbox.setPadding(new Insets(10, 0, 0, 10));
vbox.getChildren().addAll(label, table, bt1, bt2);
((Group) scene.getRoot()).getChildren().addAll(vbox);
stage.setScene(scene);
stage.show();
/*************************************************/
bt1.setOnAction(value -> {
data.add(new Person("xi.", "2023-01-01", "add Local "));
});
/*************************************************/
bt2.setOnAction(value -> {
BufferedReader br;
String strLine;
data.add(new Person("Do.", "start reading", "readHoli-1"));
try {
URL url = new URL("https://www.spiketime.de/feiertagapi/feiertage/csv/2023/2023");
URLConnection urlCon = url.openConnection(); //feastUrl.openConnection();
InputStreamReader isr = new InputStreamReader(urlCon.getInputStream()); // ERROR ---- DOES NOT WORK after this statement
data.add(new Person("Do.", "after creating streamreader", "TEST-2"));
br = new BufferedReader(isr);
while ((strLine = br.readLine()) != null) {
if (!strLine.contains("Bayern"))
continue;
System.out.println(strLine);
/* split string/check and add to holidays: here only print */
}
br.close();
isr.close();
} catch (Exception ex) {
ex.printStackTrace();
}
data.add(new Person("Do.", "end reading", "readHoli-2"));
});
}
public static class Person {
private final StringProperty firstName;
private final StringProperty lastName;
private final StringProperty email;
private Person(String fName, String lName, String email) {
this.firstName = new SimpleStringProperty(fName);
this.lastName = new SimpleStringProperty(lName);
this.email = new SimpleStringProperty(email);
}
public String getFirstName() {
return firstName.get();
}
public void setFirstName(String fName) {
firstName.set(fName);
}
public StringProperty firstNameProperty() {
return firstName;
}
public String getLastName() {
return lastName.get();
}
public void setLastName(String fName) {
lastName.set(fName);
}
public StringProperty lastNameProperty() {
return lastName;
}
public String getEmail() {
return email.get();
}
public void setEmail(String fName) {
email.set(fName);
}
public StringProperty emailProperty() {
return email;
}
}
}
All Bindings in javafx are implemented to do their best to not cause memory leaks. They do so (in the internal class BindingHelperObserver) by
keep a weak reference to the binding: this allows being claimed by garbage collection as soon as there is no strong reference path to the object
remove any listeners to their dependencies if the binding is gc'ed
The former is the reason for the problem described in the question because the cell factory creates the binding as a local reference which is collectable as soon as the method returns. Requesting an InputStream from a UrlConnection seems to trigger a garbage run, thus collecting the weakly referenced bindings of all rows.
To make them survive the life-time of its owner, the owner has to keep a strong reference to it, f.i. in the context of a cell factory:
subclass the row
let it have a field referencing the binding
install the binding/listener in its constructor
Example:
table.setRowFactory(tableView -> {
final TableRow<Person> row = new TableRow<>() {
private BooleanBinding selectedRow;
{
selectedRow = Bindings.createBooleanBinding(
() -> selectedRowIndexes.contains(getIndex()),
indexProperty(),
selectedRowIndexes);
selectedRow.addListener((observable, oldValue, newValue) -> {
pseudoClassStateChanged(selectedRowPseudoClass, newValue);
});
}
};
return row;
});
I need to make a project using javafx where there is a table (for example of users), with another table inside each row (for example orders of the user).
I would like it to be toggle-able so I would extend a row by clicking on it and then the inner table/s would be visable.
Some draw for the gui exmaple:
This will be useful i guess
import java.util.ArrayList;
import java.util.List;
import javafx.application.Application;
import javafx.beans.property.SimpleStringProperty;
import javafx.collections.FXCollections;
import javafx.collections.ObservableList;
import javafx.geometry.Insets;
import javafx.scene.Group;
import javafx.scene.Node;
import javafx.scene.Scene;
import javafx.scene.control.Label;
import javafx.scene.control.TableColumn;
import javafx.scene.control.TableRow;
import javafx.scene.control.TableView;
import javafx.scene.control.cell.PropertyValueFactory;
import javafx.scene.layout.VBox;
import javafx.scene.text.Font;
import javafx.stage.Stage;
public class TreeTableView extends Application {
private TableView<Person> table = new TableView<Person>();
public static void main(String[] args) {
launch(args);
}
#Override
public void start(Stage stage) {
Scene scene = new Scene(new Group());
stage.setTitle("Table View Sample");
stage.setWidth(450);
stage.setHeight(500);
final Label label = new Label("Address Book");
label.setFont(new Font("Arial", 20));
constructTable();
final VBox vbox = new VBox();
vbox.setSpacing(5);
vbox.setPadding(new Insets(10, 0, 0, 10));
vbox.getChildren().addAll(label, table);
((Group) scene.getRoot()).getChildren().addAll(vbox);
stage.setScene(scene);
stage.show();
}
private void constructTable() {
TableColumn<Person, String> firstNameCol = new TableColumn<Person, String>("First Name");
firstNameCol.setMinWidth(100);
firstNameCol.setCellValueFactory(new PropertyValueFactory<Person, String>("firstName"));
TableColumn<Person, String> lastNameCol = new TableColumn<Person, String>("Last Name");
lastNameCol.setMinWidth(100);
lastNameCol.setCellValueFactory(new PropertyValueFactory<Person, String>("lastName"));
TableColumn<Person, String> emailCol = new TableColumn<Person, String>("Email");
emailCol.setMinWidth(200);
emailCol.setCellValueFactory(new PropertyValueFactory<Person, String>("email"));
table.setItems(getData());
table.getColumns().addAll(firstNameCol, lastNameCol, emailCol);
table.setRowFactory(tv -> new TableRow<Person>() {
Node detailsPane ;
{
this.selectedProperty().addListener((obs, wasSelected, isNowSelected) -> {
if (isNowSelected ) {
detailsPane = constructSubTable(getItem());
this.getChildren().add(detailsPane);
} else {
this.getChildren().remove(detailsPane);
}
this.requestLayout();
});
}
#Override
protected double computePrefHeight(double width) {
if (isSelected()) {
return super.computePrefHeight(width)+detailsPane.prefHeight(60);
} else {
return super.computePrefHeight(width);
}
}
#Override
protected void layoutChildren() {
super.layoutChildren();
if (isSelected()) {
double width = getWidth();
double paneHeight = detailsPane.prefHeight(width);
detailsPane.resizeRelocate(0, getHeight()-paneHeight, width, paneHeight);
}
}
});
}
private TableView<Address> constructSubTable(Person person) {
List<Address> addresses = new ArrayList<>(); addresses.add(person.getAddress());
TableView<Address> subTable = new TableView<Address>();
TableColumn<Address, String> streetCol = new TableColumn<Address, String>("Street");
streetCol.setMinWidth(100);
streetCol.setCellValueFactory(new PropertyValueFactory<Address, String>("Street"));
TableColumn<Address, String> cityCol = new TableColumn<Address, String>("City");
cityCol.setMinWidth(100);
cityCol.setCellValueFactory(new PropertyValueFactory<Address, String>("city"));
subTable.setItems(FXCollections.observableArrayList(addresses));
subTable.getColumns().addAll(streetCol, cityCol);
subTable.setPrefHeight(50+(addresses.size()*30));
subTable.setStyle("-fx-border-color: #42bff4;");
return subTable;
}
private ObservableList<Person> getData() {
return FXCollections.observableArrayList(new Person("Jacob", "Smith", "jacob.smith#example.com","Jacob Street","NY"),
new Person("Isabella", "Johnson", "isabella.johnson#example.com","Isabella Street","DL"),
new Person("Ethan", "Williams", "ethan.williams#example.com","Ethan Street"," ML"),
new Person("Emma", "Jones", "emma.jones#example.com","Emma Street","EL"),
new Person("Michael", "Brown", "michael.brown#example.com","Michael Street","ML"));
}
public static class Person {
private final SimpleStringProperty firstName;
private final SimpleStringProperty lastName;
private final SimpleStringProperty email;
private Address address;
Person(String fName, String lName, String email,String streetS, String cityS) {
this.firstName = new SimpleStringProperty(fName);
this.lastName = new SimpleStringProperty(lName);
this.email = new SimpleStringProperty(email);
address = new Address(streetS, cityS);
}
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 Address getAddress() {
return address;
}
public void setAddress(Address address) {
this.address = address;
}
}
public static class Address{
private final SimpleStringProperty street;
private final SimpleStringProperty city;
Address(String streetS, String cityS) {
this.street = new SimpleStringProperty(streetS);
this.city = new SimpleStringProperty(cityS);
}
public String getStreet() {
return street.get();
}
public void setStreet(String streetS) {
street.set(streetS);
}
public String getCity() {
return city.get();
}
public void setCity(String cityS) {
city.set(cityS);
}
}
}
https://gist.github.com/sh9va/c81b9de44811cc860951701124941c1e
You need to use TreeTableView in Scene Build and so adding the columns you want, aside from that, you should create the TabPane layout which will contain the information you want.
p.s do not forget to make a Model, TreeTableView needs a model to create the cells.
I have a TableView which shows all the data from a Derby table, except the "ID" of the items (I don't want the user to see it.)
The method is simple, I made an SQL statement to select all the attributes but I did not ask for the ID column. I got back a TableData and this variable is shown in the TableView with all of the records (It is a simple namelist).
I want to allow the user to delete from the table with a "delete button".
So first, there should be an "OnAction" method, (when the user clicks the delete button) when we gather the ACTUAL selected row's (if not null) ID, and we send a statement to the database to delete it, where the (HIDDEN) ID of the selected item can be found at the derby table.
Since it is a Namelist, and the user is allowed to make another record with the exactly same data, in the tableview the records can be clones, with no differences. (only the ID is uniqe, but the tableview does not contain the id-s, that makes it so hard).
So how could we delete the selected line without knowing it's ID? Or how can we know the ID when it is not shown in the table? (searching on the name is not working, since there can be several records with the same name)
What is the "hidden column's mysterious solution"? :)
Your question has a simple solution. If you don't want to show the ID on the tableview, you don't have to. TableView binds with your class, in your case user, and not with its fields. When you add the values to the tableview, you can control which fields you want to show on it. Whether you show it on the tableview or not, you still have your complete user object with you (which still has the ID).
Now, there are multiple ways to do this. One of the way is to bind the id with the delete button that you want to show in each row. Whenever the delete button is pressed, delete the item from the DB and delete it from the tableview.
I have create an working example which shows how this work. I have hardcoded my values and instead of deleting it from the database, I am printing the ID value on the console.
In this example, the Person class can be considered equivalent to your user, each of which has an id and other attributes.
import javafx.application.Application;
import javafx.beans.property.SimpleIntegerProperty;
import javafx.beans.property.SimpleStringProperty;
import javafx.collections.FXCollections;
import javafx.collections.ObservableList;
import javafx.geometry.Insets;
import javafx.scene.Group;
import javafx.scene.Scene;
import javafx.scene.control.*;
import javafx.scene.control.cell.PropertyValueFactory;
import javafx.scene.image.Image;
import javafx.scene.image.ImageView;
import javafx.scene.layout.VBox;
import javafx.scene.text.Font;
import javafx.stage.Stage;
public class TableViewSample extends Application {
private TableView<Person> table = new TableView<Person>();
private final ObservableList<Person> data =
FXCollections.observableArrayList(
new Person(10, "Jacob", "Smith", "jacob.smith#example.com"),
new Person(20, "Isabella", "Johnson", "isabella.johnson#example.com"),
new Person(30, "Ethan", "Williams", "ethan.williams#example.com"),
new Person(40, "Emma", "Jones", "emma.jones#example.com"),
new Person(50, "Michael", "Brown", "michael.brown#example.com")
);
public static void main(String[] args) {
launch(args);
}
#Override
public void start(Stage stage) {
Scene scene = new Scene(new Group());
stage.setTitle("Table View Sample");
stage.setWidth(600);
stage.setHeight(500);
final Label label = new Label("Address Book");
label.setFont(new Font("Arial", 20));
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 deleteCol = new TableColumn("Delete");
deleteCol.setMinWidth(100);
deleteCol.setCellFactory(param -> new ButtonCell());
deleteCol.setCellValueFactory(
new PropertyValueFactory<Person, String>("id"));
table.setItems(data);
table.getColumns().addAll(firstNameCol, lastNameCol, emailCol, deleteCol);
final VBox vbox = new VBox();
vbox.setSpacing(5);
vbox.setPadding(new Insets(10, 0, 0, 10));
vbox.getChildren().addAll(label, table);
((Group) scene.getRoot()).getChildren().addAll(vbox);
stage.setScene(scene);
stage.show();
}
public static class Person {
private final SimpleIntegerProperty id;
private final SimpleStringProperty firstName;
private final SimpleStringProperty lastName;
private final SimpleStringProperty email;
private Person(Integer id, String fName, String lName, String email) {
this.id = new SimpleIntegerProperty(id);
this.firstName = new SimpleStringProperty(fName);
this.lastName = new SimpleStringProperty(lName);
this.email = new SimpleStringProperty(email);
}
public int getId() {
return id.get();
}
public void setId(int id) {
this.id.set(id);
}
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);
}
}
private class ButtonCell extends TableCell<Person, Integer> {
Image buttonDeleteImage = new Image("https://cdn1.iconfinder.com/data/icons/nuove/22x22/actions/fileclose.png");
final Button cellDeleteButton = new Button("", new ImageView(buttonDeleteImage));
ButtonCell() {
cellDeleteButton.setOnAction(actionEvent -> {
System.out.println("Deleted Id : " + getItem());// Make a DB call and delete the person with ID
getTableView().getItems().remove(getIndex());
});
}
#Override
protected void updateItem(Integer t, boolean empty) {
super.updateItem(t, empty);
if (!empty) {
setGraphic(cellDeleteButton);
} else {
setGraphic(null);
}
}
}
}
To do it without binding ID
You need :
deleteCol.setCellValueFactory(p -> {
return new ReadOnlyObjectWrapper<Person>((Person)p.getValue());
});
The custom ButtonCell should extend TableCell<Person, Person>
The logic to delete item becomes :
System.out.println("Deleted ID : " +
getItem().getId());// Make a DB call and delete the person with ID
getTableView().getItems().remove(getIndex());
Complete Example:
public class TableViewSample extends Application {
private TableView<Person> table = new TableView<Person>();
private final ObservableList<Person> data =
FXCollections.observableArrayList(
new Person(10, "Jacob", "Smith", "jacob.smith#example.com"),
new Person(20, "Isabella", "Johnson", "isabella.johnson#example.com"),
new Person(30, "Ethan", "Williams", "ethan.williams#example.com"),
new Person(40, "Emma", "Jones", "emma.jones#example.com"),
new Person(50, "Michael", "Brown", "michael.brown#example.com")
);
public static void main(String[] args) {
launch(args);
}
#Override
public void start(Stage stage) {
Scene scene = new Scene(new Group());
stage.setTitle("Table View Sample");
stage.setWidth(600);
stage.setHeight(500);
final Label label = new Label("Address Book");
label.setFont(new Font("Arial", 20));
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 deleteCol = new TableColumn("Delete");
deleteCol.setMinWidth(100);
deleteCol.setCellFactory(param -> new ButtonCell());
deleteCol.setCellValueFactory(p -> {
return new ReadOnlyObjectWrapper<Person>((Person)p.getValue());
});
table.setItems(data);
table.getColumns().addAll(firstNameCol, lastNameCol, emailCol, deleteCol);
final VBox vbox = new VBox();
vbox.setSpacing(5);
vbox.setPadding(new Insets(10, 0, 0, 10));
vbox.getChildren().addAll(label, table);
((Group) scene.getRoot()).getChildren().addAll(vbox);
stage.setScene(scene);
stage.show();
}
public static class Person {
private final SimpleIntegerProperty id;
private final SimpleStringProperty firstName;
private final SimpleStringProperty lastName;
private final SimpleStringProperty email;
private Person(Integer id, String fName, String lName, String email) {
this.id = new SimpleIntegerProperty(id);
this.firstName = new SimpleStringProperty(fName);
this.lastName = new SimpleStringProperty(lName);
this.email = new SimpleStringProperty(email);
}
public int getId() {
return id.get();
}
public void setId(int id) {
this.id.set(id);
}
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);
}
}
private class ButtonCell extends TableCell<Person, Person> {
Image buttonDeleteImage = new Image("https://cdn1.iconfinder.com/data/icons/nuove/22x22/actions/fileclose.png");
final Button cellDeleteButton = new Button("", new ImageView(buttonDeleteImage));
ButtonCell() {
cellDeleteButton.setOnAction(actionEvent -> {
System.out.println("Deleted ID : " +
getItem().getId());// Make a DB call and delete the person with ID
getTableView().getItems().remove(getIndex());
});
}
#Override
protected void updateItem(Person t, boolean empty) {
super.updateItem(t, empty);
if (!empty) {
setGraphic(cellDeleteButton);
} else {
setGraphic(null);
}
}
}
}
I have found an easier way, where the user can list anything, and he decides what to show and what to hide. This solution also works if we don't know what data will come to us, but We are sure that will be "ID" column we do not wan't to show in attention:
if (*ColumNameWeWantToHide*.equals(String.valueOf(columnName))) {
col.prefWidthProperty().bind(*TableViewName*.widthProperty().divide(100));
}
It makes us also able to work with ID numbers simply, but also we are abla to watch the ID if we want, but on default we do not see them. That is a good mixture of useability and easy coding.
How can I show a tooltip for each row of a TableView?
An Example:
When I put the mouse over a specific table row it give me the details of this row's data.
Thank You
Use a row factory, and add the tooltip to the TableRow:
import javafx.application.Application;
import javafx.beans.property.SimpleStringProperty;
import javafx.collections.FXCollections;
import javafx.collections.ObservableList;
import javafx.geometry.Insets;
import javafx.scene.Group;
import javafx.scene.Scene;
import javafx.scene.control.Label;
import javafx.scene.control.TableColumn;
import javafx.scene.control.TableRow;
import javafx.scene.control.TableView;
import javafx.scene.control.Tooltip;
import javafx.scene.control.cell.PropertyValueFactory;
import javafx.scene.layout.VBox;
import javafx.scene.text.Font;
import javafx.stage.Stage;
;
public class TableViewSample extends Application {
private final TableView<Person> table = new TableView<>();
private final ObservableList<Person> data =
FXCollections.observableArrayList(
new Person("Jacob", "Smith", "jacob.smith#example.com"),
new Person("Isabella", "Johnson", "isabella.johnson#example.com"),
new Person("Ethan", "Williams", "ethan.williams#example.com"),
new Person("Emma", "Jones", "emma.jones#example.com"),
new Person("Michael", "Brown", "michael.brown#example.com")
);
public static void main(String[] args) {
launch(args);
}
#Override
public void start(Stage stage) {
Scene scene = new Scene(new Group());
stage.setTitle("Table View Sample");
stage.setWidth(450);
stage.setHeight(500);
final Label label = new Label("Address Book");
label.setFont(new Font("Arial", 20));
table.setEditable(true);
table.setRowFactory(tv -> new TableRow<Person>() {
private Tooltip tooltip = new Tooltip();
#Override
public void updateItem(Person person, boolean empty) {
super.updateItem(person, empty);
if (person == null) {
setTooltip(null);
} else {
tooltip.setText(person.getFirstName()+" "+person.getLastName());
setTooltip(tooltip);
}
}
});
TableColumn<Person, String> firstNameCol = new TableColumn<>("First Name");
firstNameCol.setMinWidth(100);
firstNameCol.setCellValueFactory(
new PropertyValueFactory<>("firstName"));
TableColumn<Person, String> lastNameCol = new TableColumn<>("Last Name");
lastNameCol.setMinWidth(100);
lastNameCol.setCellValueFactory(
new PropertyValueFactory<>("lastName"));
TableColumn<Person, String> emailCol = new TableColumn<>("Email");
emailCol.setMinWidth(200);
emailCol.setCellValueFactory(
new PropertyValueFactory<>("email"));
table.setItems(data);
table.getColumns().addAll(firstNameCol, lastNameCol, emailCol);
final VBox vbox = new VBox();
vbox.setSpacing(5);
vbox.setPadding(new Insets(10, 0, 0, 10));
vbox.getChildren().addAll(label, table);
((Group) scene.getRoot()).getChildren().addAll(vbox);
stage.setScene(scene);
stage.show();
}
public static class Person {
private final SimpleStringProperty firstName;
private final SimpleStringProperty lastName;
private final SimpleStringProperty email;
private Person(String fName, String lName, String email) {
this.firstName = new SimpleStringProperty(fName);
this.lastName = new SimpleStringProperty(lName);
this.email = new SimpleStringProperty(email);
}
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);
}
}
}
You could create a dynamic TableRow implementation:
import java.util.function.Function;
import javafx.scene.control.TableRow;
import javafx.scene.control.Tooltip;
public class TooltipTableRow<T> extends TableRow<T> {
private Function<T, String> toolTipStringFunction;
public TooltipTableRow(Function<T, String> toolTipStringFunction) {
this.toolTipStringFunction = toolTipStringFunction;
}
#Override
protected void updateItem(T item, boolean empty) {
super.updateItem(item, empty);
if(item == null) {
setTooltip(null);
} else {
Tooltip tooltip = new Tooltip(toolTipStringFunction.apply(item));
setTooltip(tooltip);
}
}
}
And set it up like this in the initialize() of your controller:
personTableView.setRowFactory((tableView) -> {
return new TooltipTableRow<Person>((Person person) -> {
return person.getFirstName()+" "+person.getLastName();
});
});
This code is supposed to show a table with value in it, the columns and rows are added dinamicly , the idea of the program is to show a matrix in a tableview ... anyway it shows empty rows, What is the solution ?
private TableView MSTab;
float MS[][]=new float[n][n];
for (int s = 1; s < n+1; s++){
TableColumn a = new TableColumn(Integer.toString(s));
MSTab.getColumns().add(a);
}
ObservableList<String> row = FXCollections.observableArrayList();
for (int l=0;l<n;l++){
row.removeAll(row);
for (int s=0;s<n;s++){
row.add(Float.toString(MS[l][s]));
}
MSTab.getItems().add((ObservableList) row);
}
There is so much wrong in the code. Please have a look at the official tutorial.
SSCCE:
import javafx.application.Application;
import javafx.beans.property.SimpleStringProperty;
import javafx.collections.FXCollections;
import javafx.collections.ObservableList;
import javafx.geometry.Insets;
import javafx.scene.Group;
import javafx.scene.Scene;
import javafx.scene.control.Label;
import javafx.scene.control.TableColumn;
import javafx.scene.control.TableView;
import javafx.scene.control.TextField;
import javafx.scene.control.cell.PropertyValueFactory;
import javafx.scene.layout.VBox;
import javafx.scene.text.Font;
import javafx.stage.Stage;
public class TableViewSample extends Application {
private TableView<Person> table = new TableView<Person>();
private final ObservableList<Person> data =
FXCollections.observableArrayList(
new Person("Jacob", "Smith", "jacob.smith#example.com"),
new Person("Isabella", "Johnson", "isabella.johnson#example.com"),
new Person("Ethan", "Williams", "ethan.williams#example.com"),
new Person("Emma", "Jones", "emma.jones#example.com"),
new Person("Michael", "Brown", "michael.brown#example.com")
);
public static void main(String[] args) {
launch(args);
}
#Override
public void start(Stage stage) {
Scene scene = new Scene(new Group());
stage.setTitle("Table View Sample");
stage.setWidth(450);
stage.setHeight(500);
final Label label = new Label("Address Book");
label.setFont(new Font("Arial", 20));
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"));
table.setItems(data);
table.getColumns().addAll(firstNameCol, lastNameCol, emailCol);
final VBox vbox = new VBox();
vbox.setSpacing(5);
vbox.setPadding(new Insets(10, 0, 0, 10));
vbox.getChildren().addAll(label, table);
((Group) scene.getRoot()).getChildren().addAll(vbox);
stage.setScene(scene);
stage.show();
}
public static class Person {
private final SimpleStringProperty firstName;
private final SimpleStringProperty lastName;
private final SimpleStringProperty email;
private Person(String fName, String lName, String email) {
this.firstName = new SimpleStringProperty(fName);
this.lastName = new SimpleStringProperty(lName);
this.email = new SimpleStringProperty(email);
}
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);
}
}
}