JavaFx 2.1, 2.2 TableView update issue - javafx-2

My application uses JPA read data into TableView then modify and display them. The table refreshed modified record under JavaFx 2.0.3. Under JavaFx 2.1, 2.2, the table wouldn't refresh the update anymore. I found other people have similar issue. My plan was to continue using 2.0.3 until someone fixes the issue under 2.1 and 2.2. Now I know it is not a bug and wouldn't be fixed. Well, I don't know how to deal with this. Following are codes are modified from sample demo to show the issue. If I add a new record or delete a old record from table, table refreshes fine. If I modify a record, the table wouldn't refreshes the change until a add, delete or sort action is taken. If I remove the modified record and add it again, table refreshes. But the modified record is put at button of table. Well, if I remove the modified record, add the same record then move the record to the original spot, the table wouldn't refresh anymore. Below is a completely code, please shine some light on this.
import javafx.application.Application;
import javafx.beans.property.SimpleStringProperty;
import javafx.collections.FXCollections;
import javafx.collections.ObservableList;
import javafx.event.ActionEvent;
import javafx.event.EventHandler;
import javafx.geometry.HPos;
import javafx.geometry.Insets;
import javafx.geometry.Pos;
import javafx.scene.Group;
import javafx.scene.Scene;
import javafx.scene.control.*;
import javafx.scene.control.cell.PropertyValueFactory;
import javafx.scene.layout.GridPane;
import javafx.scene.layout.HBox;
import javafx.scene.layout.VBox;
import javafx.scene.text.Font;
import javafx.stage.Modality;
import javafx.stage.Stage;
import javafx.stage.StageStyle;
public class Main extends Application {
private TextField firtNameField = new TextField();
private TextField lastNameField = new TextField();
private TextField emailField = new TextField();
private Stage editView;
private Person fPerson;
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);
}
}
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(535);
stage.setHeight(535);
editView = new Stage();
final Label label = new Label("Address Book");
label.setFont(new Font("Arial", 20));
TableColumn firstNameCol = new TableColumn("First Name");
firstNameCol.setCellValueFactory(
new PropertyValueFactory<Person, String>("firstName"));
firstNameCol.setMinWidth(150);
TableColumn lastNameCol = new TableColumn("Last Name");
lastNameCol.setCellValueFactory(
new PropertyValueFactory<Person, String>("lastName"));
lastNameCol.setMinWidth(150);
TableColumn emailCol = new TableColumn("Email");
emailCol.setMinWidth(200);
emailCol.setCellValueFactory(
new PropertyValueFactory<Person, String>("email"));
table.setItems(data);
table.getColumns().addAll(firstNameCol, lastNameCol, emailCol);
//--- create a edit button and a editPane to edit person
Button addButton = new Button("Add");
addButton.setOnAction(new EventHandler<ActionEvent>() {
#Override
public void handle(ActionEvent event) {
fPerson = null;
firtNameField.setText("");
lastNameField.setText("");
emailField.setText("");
editView.show();
}
});
Button editButton = new Button("Edit");
editButton.setOnAction(new EventHandler<ActionEvent>() {
#Override
public void handle(ActionEvent event) {
if (table.getSelectionModel().getSelectedItem() != null) {
fPerson = table.getSelectionModel().getSelectedItem();
firtNameField.setText(fPerson.getFirstName());
lastNameField.setText(fPerson.getLastName());
emailField.setText(fPerson.getEmail());
editView.show();
}
}
});
Button deleteButton = new Button("Delete");
deleteButton.setOnAction(new EventHandler<ActionEvent>() {
#Override
public void handle(ActionEvent event) {
if (table.getSelectionModel().getSelectedItem() != null) {
data.remove(table.getSelectionModel().getSelectedItem());
}
}
});
HBox addEditDeleteButtonBox = new HBox();
addEditDeleteButtonBox.getChildren().addAll(addButton, editButton, deleteButton);
addEditDeleteButtonBox.setAlignment(Pos.CENTER_RIGHT);
addEditDeleteButtonBox.setSpacing(3);
GridPane editPane = new GridPane();
editPane.getStyleClass().add("editView");
editPane.setPadding(new Insets(3));
editPane.setHgap(5);
editPane.setVgap(5);
Label personLbl = new Label("Person:");
editPane.add(personLbl, 0, 1);
GridPane.setHalignment(personLbl, HPos.LEFT);
firtNameField.setPrefWidth(250);
lastNameField.setPrefWidth(250);
emailField.setPrefWidth(250);
Label firstNameLabel = new Label("First Name:");
Label lastNameLabel = new Label("Last Name:");
Label emailLabel = new Label("Email:");
editPane.add(firstNameLabel, 0, 3);
editPane.add(firtNameField, 1, 3);
editPane.add(lastNameLabel, 0, 4);
editPane.add(lastNameField, 1, 4);
editPane.add(emailLabel, 0, 5);
editPane.add(emailField, 1, 5);
GridPane.setHalignment(firstNameLabel, HPos.RIGHT);
GridPane.setHalignment(lastNameLabel, HPos.RIGHT);
GridPane.setHalignment(emailLabel, HPos.RIGHT);
Button saveButton = new Button("Save");
saveButton.setOnAction(new EventHandler<ActionEvent>() {
#Override
public void handle(ActionEvent event) {
if (fPerson == null) {
fPerson = new Person(
firtNameField.getText(),
lastNameField.getText(),
emailField.getText());
data.add(fPerson);
} else {
int k = -1;
if (data.size() > 0) {
for (int i = 0; i < data.size(); i++) {
if (data.get(i) == fPerson) {
k = i;
}
}
}
fPerson.setFirstName(firtNameField.getText());
fPerson.setLastName(lastNameField.getText());
fPerson.setEmail(emailField.getText());
data.set(k, fPerson);
table.setItems(data);
// The following will work, but edited person has to be added to the button
//
// data.remove(fPerson);
// data.add(fPerson);
// add and remove refresh the table, but now move edited person to original spot,
// it failed again with the following code
// while (data.indexOf(fPerson) != k) {
// int i = data.indexOf(fPerson);
// Collections.swap(data, i, i - 1);
// }
}
editView.close();
}
});
Button cancelButton = new Button("Cancel");
cancelButton.setOnAction(new EventHandler<ActionEvent>() {
#Override
public void handle(ActionEvent event) {
editView.close();
}
});
HBox saveCancelButtonBox = new HBox();
saveCancelButtonBox.getChildren().addAll(saveButton, cancelButton);
saveCancelButtonBox.setAlignment(Pos.CENTER_RIGHT);
saveCancelButtonBox.setSpacing(3);
VBox editBox = new VBox();
editBox.getChildren().addAll(editPane, saveCancelButtonBox);
Scene editScene = new Scene(editBox);
editView.setTitle("Person");
editView.initStyle(StageStyle.UTILITY);
editView.initModality(Modality.APPLICATION_MODAL);
editView.setScene(editScene);
editView.close();
final VBox vbox = new VBox();
vbox.setSpacing(5);
vbox.getChildren().addAll(label, table, addEditDeleteButtonBox);
vbox.setPadding(new Insets(10, 0, 0, 10));
((Group) scene.getRoot()).getChildren().addAll(vbox);
stage.setScene(scene);
stage.show();
}
}

See the answer to Updating rows in Tableview. Add these getters and it will just work.
Additionally since the data is an ObservableList which is set as items to tableView, any changes to this data list will be reflected to the table.getItems() too. Namely no need to table.setItems(data) again.

I have found a simple workaround for triggering the refresh of the TableView in JavaFX 2.1 TableView refresh items. It solved the issue for me.
Add this to your code:
tableView.getColumns().get(0).setVisible(false);
tableView.getColumns().get(0).setVisible(true);

looking into the TableView.java code, there's private refresh() which just executes
getProperties().put(TableViewSkinBase.REFRESH, Boolean.TRUE);
At last, the code below worked for me(Java8). (be careful, the constant's name is not REFRESH but RECREATE)
tableView.getProperties().put(TableViewSkinBase.RECREATE, Boolean.TRUE);
(reading javafx's code, this will force cell re-creation)

Notification-based updates of JavaFX controls typically require that the properties of the data model object backing your GUI meet the minimum definition for a JavaFX Bean.
The following exemplifies the minimum code needed in order for a JavaFX property to satisfy these requirements:
public class Client extends DB {
private IntegerProperty id = new SimpleIntegerProperty();
private StringProperty lastName = new SimpleStringProperty();
private StringProperty firstName = new SimpleStringProperty();
public final int getID() {return this.id.get(); }
void setID(int id) { this.id.set(id); }
public final IntegerProperty idProperty() { return this.id; }
public final String getLastName() { return this.lastName.get(); }
public final void setLastName(String ln) { this.lastName.set(ln); }
public final StringProperty lastNameProperty() { return this.lastName; }
public final String getFirstName() { return this.firstName.get(); }
public final void setFirstName(String fn) { this.firstName.set(fn); }
public final StringProperty firstNameProperty() { return this.firstName; }
:
:
}
Glancing over your code, it does not appear that your properties satisfy the requirements for a JavFX Bean. As such, automatic notification-based updates will not occur.

I have the same problem, and not being able to add SimpleStringProperty to the POJO's used by JPA makes this a bit problematic. But it seems to me that this should be fixable issue because I have noticed the following behavior:
In my application, clicking on a row in the table populates some text fields on the screen, that the user can then edit.
At that point the user can save the data, or create a new item with the same or changed data. If the user creates a new item, which is then inserted into the observable list that the tableview represents, the change is immediately reflected in the contents of the tableview. However if the user just saves the change the new data is not reflected in the table. To put the new data in the list I'm simply doing
trialList.set(currentIndex, tempTrial);
And here's what I think points to this as a fixable issue: if I scroll the affected row out of view on the table and then scroll it back, the 'new' value(s) are now presented.
Hopefully, this can be fixed. Sorry this isn't an answer, so to speak, but might provide some insight for a fix.

this worked for me
#FXML
private void refreshTableView()
{
firstNameCol.setVisible(false);
lastNameCol.setVisible(false);
emailCol.setVisible(false);
firstNameCol.setVisible(true);
lastNameCol.setVisible(true);
emailCol.setVisible(true);
}

I had the same problem and after some search this is my workaround. I found that if the columns are removed and then re-added the table is updated.
public static <T,U> void refreshTableView(final TableView<T> tableView, final List<TableColumn<T,U>> columns, final List<T> rows) {
if (tableView == null) {
throw new NullPointerException();
}
if (columns == null) {
throw new NullPointerException();
}
if (rows == null) {
throw new NullPointerException();
}
tableView.getColumns().clear();
tableView.getColumns().addAll(columns);
ObservableList<T> list = FXCollections.observableArrayList(rows);
tableView.setItems(list);
}
Example of usage:
refreshTableView(myTableView, Arrays.asList(col1, col2, col3), rows);

Related

Why does adding JavaFX TableViews to a VBox make other nodes disappear?

EDIT: Forgot the code...
I have an app that let's the user select CSV files for viewing. I'm using JavaFX TableViews to display the data.
For one page, the user can type into a special text box. It's a custom class I made called AutoCompleteTextArea, which extends RichTextFX's StyleClassedTextArea. On other pages, this text box should be hidden. When I have just one TableView, things work fine.
vbox.getChildren().addAll(menuBar, title, subtitle, reqBox, reqTable);
But I need other pages with different TableViews. If I add another TableView to the VBox, my AutoCompleteTextArea goes away!
vbox.getChildren().addAll(menuBar, title, subtitle, reqBox, reqTable, tempTable);
The TableViews do not appear to be overlapping... Any idea why the AutoCompleteTextArea is disappearing? The other weird thing is that if I substitute a regular TextField for the AutoCompleteTextArea, things work fine!
Here's my code. You will need RichTextFX on your build path in order to run it. Use the View Menu to see the problem. The first menu item shows the AutoCompleteTextArea (in the working case). The second menu item shows a different TableView, but this is the broken case - the AutoCompleteTextArea is gone from the first page.
Line 132 is the line in question.
I hope someone is up for the challenge!
More background:
I originally wanted to have just one TableView, and update it's contents based on the user's selection in the View Menu. But I couldn't find a good way to do that, and now here I am again... (see this post: How do I clone a JavaFX TableView?)
package FLOOR;
// --- Imports
import java.util.LinkedList;
import java.util.List;
import java.util.SortedSet;
import java.util.TreeSet;
import org.fxmisc.richtext.StyleClassedTextArea;
import javafx.application.Application;
import javafx.beans.property.StringProperty;
import javafx.collections.ObservableList;
import javafx.event.ActionEvent;
import javafx.event.EventHandler;
import javafx.geometry.Pos;
import javafx.scene.Scene;
import javafx.scene.control.*;
import javafx.scene.layout.VBox;
import javafx.scene.text.Font;
import javafx.scene.text.TextAlignment;
import javafx.stage.Stage;
// --- Main Class
public class Example extends Application {
// --- All Pages
final Page[] pages = new Page[] {
new Page("Welcome!",
"Welcome Page"),
new Page("Page 1", "Shows Table_1"),
new Page("Page 2", "Shows Table_2"),
};
// --- All Tables
TableView<ObservableList<StringProperty>> reqTable = new TableView<>();
TableView<ObservableList<StringProperty>> tempTable = new TableView<>();
//TextField reqBox = new TextField();
AutoCompleteTextArea reqBox = new AutoCompleteTextArea();
// --- Current Page
final Label title = new Label();
final Label subtitle = new Label();
// --- Main
public static void main(String[] args) { launch(args); }
// --- Start
#Override
public void start(Stage stage) {
// --- Menus
// --- File Menu
// --- Import Submenu
Menu menuFile = new Menu("File");
Menu importMenu = new Menu("Import");
MenuItem reqOption = new MenuItem("Requirements");
MenuItem tempOption = new MenuItem("Templates");
importMenu.getItems().addAll(reqOption, tempOption);
//Import Requirements
reqOption.setOnAction(new EventHandler<ActionEvent>() {
public void handle(ActionEvent e) {
//TODO
}
});
//Import Templates
tempOption.setOnAction(new EventHandler<ActionEvent>() {
public void handle(ActionEvent e) {
//TODO
}
});
//Export
MenuItem export = new MenuItem("Export");
export.setOnAction(new EventHandler<ActionEvent>() {
public void handle(ActionEvent t) {
//TODO
}
});
//Exit
MenuItem exit = new MenuItem("Exit");
exit.setOnAction(new EventHandler<ActionEvent>() {
public void handle(ActionEvent t) {
System.exit(0);
}
});
menuFile.getItems().addAll(importMenu, export, new SeparatorMenuItem(), exit);
// --- View Menu
Menu menuView = new Menu("View");
//Page1
MenuItem viewRequirements = new MenuItem("Requirements");
viewRequirements.setOnAction(new EventHandler<ActionEvent>() {
public void handle(ActionEvent t) {
getPage1();
}
});
//Page2
MenuItem viewTemplates = new MenuItem("Templates");
viewTemplates.setOnAction(new EventHandler<ActionEvent>() {
public void handle(ActionEvent t) {
getPage2();
}
});
menuView.getItems().addAll(viewRequirements, viewTemplates);
// --- Menu Bar
MenuBar menuBar = new MenuBar();
menuBar.getMenus().addAll(menuFile, menuView);
// --- VBox
final VBox vbox = new VBox();
vbox.setAlignment(Pos.TOP_CENTER);
vbox.setSpacing(10);
reqTable.setMinHeight(300);
tempTable.setMinHeight(300);
reqTable.translateYProperty().set(30);
tempTable.translateYProperty().set(-275);
reqTable.setVisible(false);
tempTable.setVisible(false);
reqBox.setVisible(false);
// --- Welcome Page
title.setFont(new Font("Arial", 24));
title.translateYProperty().set(10);
title.setText(pages[0].title);
subtitle.setText(pages[0].subtitle);
subtitle.setMinHeight(30);
subtitle.setTextAlignment(TextAlignment.CENTER);
// --- Show FLOOR
vbox.getChildren().addAll(menuBar, title, subtitle, reqBox, reqTable);
//vbox.getChildren().addAll(menuBar, title, subtitle, reqBox, reqTable, tempTable);
Scene scene = new Scene(vbox, 900, 500);
stage.setScene(scene);
stage.setTitle("FLOOR");
stage.show();
}
// --- Methods
// Page Getters
private void getPage1() {
title.setFont(new Font("Arial", 24));
title.translateYProperty().set(10);
title.setText(pages[1].title);
subtitle.setText(pages[1].subtitle);
subtitle.setMinHeight(20);
reqBox.setVisible(true);
reqTable.setVisible(true);
tempTable.setVisible(false);
}
private void getPage2() {
title.setFont(new Font("Arial", 24));
title.translateYProperty().set(10);
title.setText(pages[2].title);
subtitle.setText(pages[2].subtitle);
subtitle.setMinHeight(20);
reqBox.setVisible(false);
reqTable.setVisible(false);
tempTable.setVisible(true);
}
// --- Classes
// Page
private class Page {
public String title;
public String subtitle;
public Page(String title, String subtitle) {
this.title = title;
this.subtitle = subtitle;
}
}
// AutoCompleteTextArea
public class AutoCompleteTextArea extends StyleClassedTextArea {
public final SortedSet<String> entries;
public ContextMenu entriesPopup;
public AutoCompleteTextArea() {
super();
entries = new TreeSet<>();
entriesPopup = new ContextMenu();
}
public SortedSet<String> getEntries() { return entries; }
public void populatePopup(List<String> searchResult) {
List<CustomMenuItem> menuItems = new LinkedList<>();
int maxEntries = 20;
int count = Math.min(searchResult.size(), maxEntries);
for (int i = 0; i < count; i++) {
final String result = searchResult.get(i);
Label entryLabel = new Label(result);
CustomMenuItem item = new CustomMenuItem(entryLabel, true);
menuItems.add(item);
}
entriesPopup.getItems().clear();
entriesPopup.getItems().addAll(menuItems);
}
}
}

Override TreeTableCell rendering in JavaFX

I am trying to change the rendering of a TextFieldTreeTableCell to show a string as a Hyperlink as opposed to plaintext, to no avail. It seems as though it should be doable using setSkin, but something like
setSkin((new HyperLink()).getSkin());
or
setSkin((new HyperLink(getItem())).getSkin());
does not help. Any insight on how this could be done?
What you are doing wrong
You are not using the right function to customize your cell: setSkin is is used for creating custom control skins and is generic to all kinds of controls not just cells, you should a use a cell factory instead.
You are not using the right superclass: TextFieldTreeTableCell is for creating a cell which contains a label that can be made into an editable TextField when you click on it. Such functionality is not useful when you want to "display a non-editable, clickable URL".
Approach you should use
Cells have a specific method for controlling their rendering which is preferred to the skin mechanism when working with cells. This cell specific mechanism is called a cell factory and is documented with an example in the Cell documentation.
TreeTableColumns allow you to set a cell factory on the column to control the rendering of the column cells. The relevant code for rendering a Hyperlink in a cell is below:
emailColumn.setCellFactory(param -> new TreeTableCell<Employee, String>() {
private Hyperlink link = new Hyperlink();
{
setContentDisplay(ContentDisplay.GRAPHIC_ONLY);
}
#Override
protected void updateItem(String item, boolean empty) {
super.updateItem(item, empty);
if (item == null || empty) {
setGraphic(null);
return;
}
link.setText(item);
link.setVisited(getTreeTableRow().getItem().isVisited());
link.setOnAction(event -> {
getTreeTableRow().getItem().setVisited(true);
sendLabel.setText("Send mail to: " + item);
});
setGraphic(link);
}
});
Sample Application
In the screen shot below, the user has just linked on the hyperlink for anna.black#example.com.
The sample code is a modified version of the code from the Oracle TreeTableView tutorial. The addition of a visited property to the Employee class is necessary to keep track of which items in the TreeTableView have been clicked on, so that the Hyperlink visited property can be appropriately set when the cell is updated.
import javafx.application.Application;
import javafx.beans.property.*;
import javafx.scene.Scene;
import javafx.scene.control.*;
import javafx.scene.image.*;
import javafx.scene.layout.VBox;
import javafx.scene.paint.Color;
import javafx.stage.Stage;
import java.util.*;
public class TreeTableViewSample extends Application {
private List<Employee> employees = Arrays.asList(
new Employee("Ethan Williams", "ethan.williams#example.com"),
new Employee("Emma Jones", "emma.jones#example.com"),
new Employee("Michael Brown", "michael.brown#example.com"),
new Employee("Anna Black", "anna.black#example.com"),
new Employee("Rodger York", "roger.york#example.com"),
new Employee("Susan Collins", "susan.collins#example.com"));
private final ImageView depIcon = new ImageView (
new Image("http://icons.iconarchive.com/icons/custom-icon-design/flatastic-10/16/Bear-icon.png")
);
final TreeItem<Employee> root =
new TreeItem<>(new Employee("Sales Department", ""), depIcon);
public static void main(String[] args) {
Application.launch(TreeTableViewSample.class, args);
}
final Label sendLabel = new Label();
#Override
public void start(Stage stage) {
root.setExpanded(true);
employees.forEach((employee) -> root.getChildren().add(new TreeItem<>(employee)));
stage.setTitle("Tree Table View Sample");
final Scene scene = new Scene(new VBox(), 400, 400);
scene.setFill(Color.LIGHTGRAY);
VBox sceneRoot = (VBox) scene.getRoot();
TreeTableColumn<Employee, String> empColumn =
new TreeTableColumn<>("Employee");
empColumn.setPrefWidth(150);
empColumn.setCellValueFactory(
(TreeTableColumn.CellDataFeatures<Employee, String> param) ->
new ReadOnlyStringWrapper(param.getValue().getValue().getName())
);
TreeTableColumn<Employee, String> emailColumn =
new TreeTableColumn<>("Email");
emailColumn.setPrefWidth(190);
emailColumn.setCellValueFactory(
(TreeTableColumn.CellDataFeatures<Employee, String> param) ->
new ReadOnlyStringWrapper(param.getValue().getValue().getEmail())
);
emailColumn.setCellFactory(param -> new TreeTableCell<Employee, String>() {
private Hyperlink link = new Hyperlink();
{
setContentDisplay(ContentDisplay.GRAPHIC_ONLY);
}
#Override
protected void updateItem(String item, boolean empty) {
super.updateItem(item, empty);
if (item == null || empty) {
setGraphic(null);
return;
}
link.setText(item);
link.setVisited(getTreeTableRow().getItem().isVisited());
link.setOnAction(event -> {
getTreeTableRow().getItem().setVisited(true);
sendLabel.setText("Send mail to: " + item);
});
setGraphic(link);
}
});
TreeTableView<Employee> treeTableView = new TreeTableView<>(root);
treeTableView.getColumns().setAll(empColumn, emailColumn);
sceneRoot.getChildren().addAll(treeTableView, sendLabel);
stage.setScene(scene);
stage.show();
}
public class Employee {
private SimpleStringProperty name;
private SimpleStringProperty email;
private SimpleBooleanProperty visited;
public SimpleStringProperty nameProperty() {
if (name == null) {
name = new SimpleStringProperty(this, "name");
}
return name;
}
public SimpleStringProperty emailProperty() {
if (email == null) {
email = new SimpleStringProperty(this, "email");
}
return email;
}
private Employee(String name, String email) {
this.name = new SimpleStringProperty(name);
this.email = new SimpleStringProperty(email);
this.visited = new SimpleBooleanProperty(false);
}
public String getName() {
return name.get();
}
public void setName(String fName) {
name.set(fName);
}
public String getEmail() {
return email.get();
}
public void setEmail(String fName) {
email.set(fName);
}
public void setVisited(boolean visited) {
this.visited.set(visited);
}
public boolean isVisited() {
return visited.get();
}
}
}

How to unselect a selected table row upon second click/selection in javaFX

On my JavaFX table, when I click on a row, it selects that row. Now when I click for the second time on same row which was previously selected, I want to deselect that particular row. Is it possible ? Please share some example code, if its possible.
Below piece of code worked for this requirement.
tableView.setRowFactory(new Callback<TableView<Person>, TableRow<Person>>() {
#Override
public TableRow<Person> call(TableView<Person> tableView2) {
final TableRow<Person> row = new TableRow<>();
row.addEventFilter(MouseEvent.MOUSE_PRESSED, new EventHandler<MouseEvent>() {
#Override
public void handle(MouseEvent event) {
final int index = row.getIndex();
if (index >= 0 && index < tableView.getItems().size() && tableView.getSelectionModel().isSelected(index) ) {
tableView.getSelectionModel().clearSelection();
event.consume();
}
}
});
return row;
}
});
Used the same Person class from oracle's table view example. Original answer was given by #James_D in oracle's forum.
Basically you can choose anything invalid as the index. Generally -1 is preferred
table.getSelectionModel().select(-1);
which calls the int select. Alternative:
table.getSelectionModel().select(null);
which calls the object select
if you want to see the whole code used/confirm for this
public class Main extends Application {
#SuppressWarnings("unchecked")
#Override
public void start(Stage stage) {
Scene scene = new Scene(new Group());
TableView<Person> table = new TableView<Person>();
stage.setTitle("Table View Sample");
stage.setWidth(300);
stage.setHeight(500);
final Label label = new Label("Address Book");
label.setFont(new Font("Arial", 20));
table.setEditable(true);
TableColumn<Person, String> firstNameCol = new TableColumn<Person, String>("Test Name");
firstNameCol.setCellValueFactory(new PropertyValueFactory<Person, String>("name"));
table.getColumns().addAll(firstNameCol);
final VBox vbox = new VBox();
vbox.setSpacing(5);
vbox.setPadding(new Insets(10, 0, 0, 10));
vbox.getChildren().addAll(label, table);
table.itemsProperty().get().add(new Person("Hans"));
table.itemsProperty().get().add(new Person("Dieter"));
((Group) scene.getRoot()).getChildren().addAll(vbox);
table.getSelectionModel().select(-1);
stage.setScene(scene);
stage.show();
}
public static void main(String[] args) {
launch(args);
}
public class Person {
final StringProperty name = new SimpleStringProperty();
Person(String name) {
this.name.set(name);
}
public StringProperty nameProperty() { return this.name; }
}
}

JavaFx ProgressBar Bind SimpleDoubleProperty

I am using a progressbar in a tableview, where I'm using bind on his property so that when the value is changed, progressbar is started:
private SimpleDoubleProperty progressBar;
public Tabela(Double progressBar) {
this.progressBar = new SimpleDoubleProperty(progressBar);
}
public Double getProgressBar() {
return progressBar.get();
}
public DoubleProperty getProgressBarProperty() {
return progressBar;
}
public void setProgressBar(Double progressBar) {
this.progressBar.set(progressBar);
}
public void setProgressBar(SimpleDoubleProperty progressBar) {
this.progressBar = progressBar;
}
And in my Hbox am using progressbar as follows:
final ProgressBar progress = new ProgressBar();
progress.setMinWidth(324.0);
progress.progressProperty().bind(tabela.getProgressBarProperty());
So I'm using a so that after a certain time the value is changed, ie I will control the progressbar. The value is changed, but in my tableview nothing happens, but when I change the column position to another, the progressbar is running.
The same happens with "label", i changed for "text" and it work, but if the progressbar I have to use.
have a way to force the 'tableview' refresh?
You need to follow the JavaFX naming standard to get TableViews and other widgets to properly refresh when an observable object changes. For instance, you should rename getProgressBarProperty() to progressBarProperty().
See: How to update TableView Row using javaFx
There would be something wrong in this source?
class table
...
public Tabela(String nome, Double progressBar, String etapa) {
this.nome = nome;
this.progressBar = new SimpleDoubleProperty(progressBar);
this.etapa = new SimpleStringProperty(etapa);
}
....
Add new line.
private void preencheListaNomeTabelas() {
getLista().add(new Tabela("Test", 0.0, "Test Text"));
Add hbox in table.
columTabela.setCellValueFactory(new PropertyValueFactory<Tabela, String>("nome"));
columSituacao.setCellFactory(new Callback<TableColumn<Tabela, Double>, TableCell<Tabela, Double>>() {
public TableCell<Tabela, Double> call(TableColumn<Tabela, Double> p) {
final HBox box = new HBox();
box.setPrefHeight(25.0);
final ProgressBar progressBar = new ProgressBar(-1);
final Text text = new Text();
**text.textProperty().bind(..); //I would use here the**
BorderPane border = new BorderPane();
border.setTop(text);
border.setBottom(progressBar);
BorderPane.setAlignment(text, Pos.CENTER);
box.getChildren().add(border);
final TableCell cell = new TableCell<Tabela, Double>() {
#Override
protected void updateItem(Double t, boolean bln) {
super.updateItem(t, bln);
if (bln) {
setText(null);
setGraphic(null);
} else {
progressBar.setProgress(t);
progressBar.prefWidthProperty().bind(this.widthProperty());
setGraphic(box);
setContentDisplay(ContentDisplay.GRAPHIC_ONLY);
}
}
};
cell.setAlignment(Pos.CENTER);
return cell;
}
});
columSituacao.setCellValueFactory(new PropertyValueFactory<Tabela, Double>("progress"));
columSituacao.setText("Progresso");
tableView.getItems().addAll(lista);
tableView.getSelectionModel().selectFirst();
task
Task t = new Task() {
#Override
protected Object call() throws Exception {
Thread.sleep(10000);
getLista().get(0).setEtapa("Lendo PostgreSQL");
getLista().get(0).setProgressBar(-1.0);
return null;
}
};
new Thread(t).start();

TitledPane - Event

I have two distinct components: a TableView and a TitledPane next to the table.
What I´m trying to do is to redimension the tableview but only when the titledpane expands or collapse.
When the titledpane collapse the tableview gets bigger and when it expands the tableview gets smaller.
I don´t know what action should I take.
Anybody knows the solution?
Regards
Check out the sample code below:
import javafx.application.Application;
import javafx.beans.property.SimpleStringProperty;
import javafx.beans.value.ChangeListener;
import javafx.beans.value.ObservableValue;
import javafx.collections.FXCollections;
import javafx.collections.ObservableList;
import javafx.scene.Scene;
import javafx.scene.control.TableColumn;
import javafx.scene.control.TableView;
import javafx.scene.control.TitledPane;
import javafx.scene.control.cell.PropertyValueFactory;
import javafx.scene.layout.HBox;
import javafx.scene.text.Text;
import javafx.stage.Stage;
public class MyDemo extends Application {
private TableView<Person> tableview = new TableView<Person>();
// Suppose your preferred height values for those 2 component are as follows:
private double TABLE_MIN_HEIGHT = 30.0;
private double TABLE_MAX_HEIGHT = 500.0;
private double TITLED_PANE_HEIGHT; // will be determined
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) {
Application.launch(args);
}
#Override
public void start(Stage stage) {
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"));
tableview.setItems(data);
tableview.getColumns().addAll(firstNameCol, lastNameCol, emailCol);
final TitledPane titledPane = new TitledPane("TitledPane", new Text("Content\n\n\n\n"));
titledPane.setAnimated(false); // we need to temporarily disable
// animation to get the titledpanes computed height correctly.
// Force to min height of table view
tableview.setMaxHeight(TABLE_MIN_HEIGHT);
tableview.setMinHeight(TABLE_MIN_HEIGHT);
// Here you have 2 options
int option = 2;
if (option == 1) {
// 1st simply force the table view height to its preferred max value
// when the titled pane's expanded property changed:
titledPane.expandedProperty().addListener(new ChangeListener<Boolean>() {
#Override
public void changed(ObservableValue<? extends Boolean> observable, Boolean oldValue, Boolean newValue) {
tableview.setMaxHeight(newValue ? TABLE_MIN_HEIGHT : TABLE_MAX_HEIGHT);
tableview.setMinHeight(newValue ? TABLE_MIN_HEIGHT : TABLE_MAX_HEIGHT);
}
});
} else if (option == 2) {
// 2nd. Similar to first but with "animation". Here observe height changes of titled pane:
titledPane.heightProperty().addListener(new ChangeListener<Number>() {
#Override
public void changed(ObservableValue<? extends Number> observable, Number oldValue, Number newValue) {
tableview.setMaxHeight(TABLE_MAX_HEIGHT - (TABLE_MAX_HEIGHT * (newValue.doubleValue() / TITLED_PANE_HEIGHT)));
tableview.setMinHeight(TABLE_MAX_HEIGHT - (TABLE_MAX_HEIGHT * (newValue.doubleValue() / TITLED_PANE_HEIGHT)));
}
});
}
HBox hBox = new HBox(10);
hBox.getChildren().addAll(tableview, titledPane);
Scene scene = new Scene(hBox);
stage.setTitle("Table View Sample");
stage.setWidth(650);
stage.setHeight(700);
stage.setScene(scene);
TITLED_PANE_HEIGHT = titledPane.getHeight();
System.out.println("TITLED_PANE_HEIGHT = " + TITLED_PANE_HEIGHT);
stage.show();
// Determine the titledPane computed height value after stage has been shown.
TITLED_PANE_HEIGHT = titledPane.getHeight();
System.out.println("TITLED_PANE_HEIGHT = " + TITLED_PANE_HEIGHT);
// .. then enable animation
titledPane.setAnimated(true);
}
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);
}
}
}
It is demo, improve it according to your needs. There maybe other approaches as well. HTH.

Resources