Javafx TableView data refresh - javafx

I have combobox, tableview as TableView<Person> and 3 columns.
I want to populate the updated data in TableView as per item I select in combobox.
My Issue: every time I select the item in combobox, the data in table view is added. I want the details to be displayed for the selected item only.
Code:
public class MyTableController {
private TableView<Person> personTableView;
private ObservableList<Person> personTableViewData= FXCollections.observableArrayList();
initialize() {
comboBox.valueProperty().addListener(new ChangeListener<String>() {
#Override public void changed(ObservableValue ov, String oldValue, String newValue) {
List<Person> filteredPersonList =
personList.stream().filter(person -> person.getFirstName().contains(newValue)).collect(Collectors.toList());
personTableViewData.addAll(filteredPersonList);
personTableView.setItems(personTableViewData);
}
}

Use ObservableList.setAll instead of ObservableList.addAll or alternatively create a new ObservableList. Otherwise you're adding new items to the same list on every change instead of replacing the items:
initialize() {
personTableView.setItems(personTableViewData);
comboBox.valueProperty().addListener(new ChangeListener<String>() {
#Override public void changed(ObservableValue ov, String oldValue, String newValue) {
List<Person> filteredPersonList =
personList.stream().filter(person -> person.getFirstName().contains(newValue)).collect(Collectors.toList());
personTableViewData.setAll(filteredPersonList);
}
}

Related

JavaFx TableView get changed rows

I have a problem with JavaFx.
I have a table view, which shows the Data I want.
Now I change the Data in the TableView.
My changeListener works, so far so good.
Now I want the rows of the table with changes in it to appear in a different color.
But I just can't find a way to get the specific row.
I tried to google the solution but all I can find is how to get a selected row.
But there won't be any user input. The data just refreshes.
Can you help me?
Probably I was just to stupid to find the right keywords.
I think of something like:
tableview.getRow(indexOfChangedRow).setStyle
The TableRow maintains a property called Item, which holds the data for the row. You need a RowFactory that binds the background to some value in your Table data. Generally, your Table data is going to have fields composed of ObservableValues, like Properties. So you end up with a Property (the Table data Model), which is composed of Properties. This means that you'll need a ChangeListener to manually reset the Binding on the Background property of the TableRow whenever the Item property is changed.
Here's an example:
public class TableBackground extends Application {
private ObservableList<TableModel> tableData = FXCollections.observableArrayList();
#Override
public void start(Stage primaryStage) throws Exception {
primaryStage.setScene(new Scene(createContent()));
primaryStage.show();
}
private Parent createContent() {
BorderPane borderPane = new BorderPane();
borderPane.setCenter(createTable());
borderPane.setBottom(createAddBox());
return borderPane;
}
private Node createTable() {
TableView<TableModel> tableView = new TableView<>();
TableColumn<TableModel, String> firstColumn = new TableColumn<>("Field 1");
firstColumn.setCellValueFactory(p -> p.getValue().field1Property());
tableView.getColumns().add(firstColumn);
TableColumn<TableModel, String> secondColumn = new TableColumn<>("Field 2");
secondColumn.setCellValueFactory(p -> p.getValue().field2Property());
tableView.getColumns().add(secondColumn);
tableView.setItems(tableData);
Background redBackground = new Background(new BackgroundFill(Color.RED, null, null));
Background blueBackground = new Background(new BackgroundFill(Color.CORNFLOWERBLUE, null, null));
tableView.setRowFactory(t -> {
TableRow<TableModel> row = new TableRow<TableModel>();
row.itemProperty().addListener((ob, oldValue, newValue) -> {
row.backgroundProperty().bind(Bindings.createObjectBinding(() -> newValue.isNewRow() ? redBackground : blueBackground,
newValue.newRowProperty()));
});
return row;
});
return tableView;
}
private Node createAddBox() {
TextField textField1 = new TextField();
TextField textField2 = new TextField();
Button button = new Button("Add Row");
button.setOnAction(evt -> {
tableData.forEach(dataItem -> dataItem.setNewRow(false));
tableData.add(new TableModel(textField1.getText(), textField2.getText()));
});
return new HBox(10, textField1, textField2, button);
}
}
And the TableModel would look like this:
public class TableModel {
private StringProperty field1 = new SimpleStringProperty("");
private StringProperty field2 = new SimpleStringProperty("");
private BooleanProperty newRow = new SimpleBooleanProperty(true);
public TableModel(String field1Data, String field2Data) {
field1.set(field1Data);
field2.set(field2Data);
setNewRow(true);
}
public String getField1() {
return field1.get();
}
public StringProperty field1Property() {
return field1;
}
public void setField1(String field1) {
this.field1.set(field1);
}
public String getField2() {
return field2.get();
}
public StringProperty field2Property() {
return field2;
}
public void setField2(String field2) {
this.field2.set(field2);
}
public boolean isNewRow() {
return newRow.get();
}
public BooleanProperty newRowProperty() {
return newRow;
}
public void setNewRow(boolean newRow) {
this.newRow.set(newRow);
}
}
The important part is the RowFactory:
Background redBackground = new Background(new BackgroundFill(Color.RED, null, null));
Background blueBackground = new Background(new BackgroundFill(Color.CORNFLOWERBLUE, null, null));
tableView.setRowFactory(t -> {
TableRow<TableModel> row = new TableRow<TableModel>();
row.itemProperty().addListener((ob, oldValue, newValue) -> {
row.backgroundProperty().bind(Bindings.createObjectBinding(() -> newValue.isNewRow() ? redBackground : blueBackground,
newValue.newRowProperty()));
});
return row;
});
It's a plain vanilla row except that it has a ChangeListener on row.itemProperty() which puts a Binding on row.backgroundProperty() based on value of new items TableModel.newRowProperty(). If it's new, then the background is red, otherwise it's blue.
The Button OnAction event, sets all of the existing rows newRowProperty() to false, and then adds a new row with newRowProperty() set to true. This means that all of the existing rows will turn blue, and the new row will be red.

Bind CheckBoxTableCell to BooleanBinding

I want to bind a CheckBox in a TableViewCell to a BooleanBinding. The following sample consists of a TableView with a column name and isEffectiveRequired. The checkbox in the column is bound to the Expression:
isRequired.or(name.isEqualTo("X"))
So an item is "effectivly required" when the item in the row is required OR the name is an X, then the expression should be true.
Unfortunately the CheckBox does not reflect the change. For debugging I added a textfield, showing the nameProperty, requiredProperty and the computed effectiveRequiredProperty.
Interestingly when returning just the isRequiredProperty instead of the binding the checkbox works.
public ObservableBooleanValue effectiveRequiredProperty() {
// Bindings with this work:
// return isRequired;
// with this not
return isRequired.or(name.isEqualTo(SPECIAL_STRING));
}
So what is the difference between a Property and a ObservableValue in regard to a CheckBox?
public class TableCellCBBinding extends Application {
public static void main(String[] args) {
launch(args);
}
#Override
public void start(Stage primaryStage) throws Exception {
init(primaryStage);
primaryStage.show();
}
private void init(Stage primaryStage) {
primaryStage.setScene(new Scene(buildContent()));
}
private Parent buildContent() {
TableView<ViewModel> tableView = new TableView<>();
tableView.setItems(sampleEntries());
tableView.setEditable(true);
tableView.getColumns().add(buildRequiredColumn());
tableView.getColumns().add(buildNameColumn());
// Add a Textfield to show the values for the first item
// As soon as the name is set to "X", the effectiveRequiredProperty should evaluate to true and the CheckBox should reflect this but it does not
TextField text = new TextField();
ViewModel firstItem = tableView.getItems().get(0);
text.textProperty()
.bind(Bindings.format("%s | %s | %s", firstItem.nameProperty(), firstItem.isRequiredProperty(), firstItem.effectiveRequiredProperty()));
return new HBox(text, tableView);
}
private TableColumn<ViewModel, String> buildNameColumn() {
TableColumn<ViewModel, String> nameColumn = new TableColumn<>("Name");
nameColumn.setCellValueFactory(new PropertyValueFactory<>("name"));
nameColumn.setCellFactory(TextFieldTableCell.forTableColumn());
nameColumn.setEditable(true);
return nameColumn;
}
private TableColumn<ViewModel, Boolean> buildRequiredColumn() {
TableColumn<ViewModel, Boolean> requiredColumn = new TableColumn<>("isEffectiveRequired");
requiredColumn.setMinWidth(50);
// This is should bind my BindingExpression from to ViewModel to the CheckBox
requiredColumn.setCellValueFactory( p -> p.getValue().effectiveRequiredProperty());
requiredColumn.setCellFactory( CheckBoxTableCell.forTableColumn(requiredColumn));
return requiredColumn;
}
private ObservableList<ViewModel> sampleEntries() {
return FXCollections.observableArrayList(
new ViewModel(false, "A"),
new ViewModel(true, "B"),
new ViewModel(false, "C"),
new ViewModel(true, "D"),
new ViewModel(false, "E"));
}
public static class ViewModel {
public static final String SPECIAL_STRING = "X";
private final StringProperty name;
private final BooleanProperty isRequired;
public ViewModel(boolean isRequired, String name) {
this.name = new SimpleStringProperty(this, "name", name);
this.isRequired = new SimpleBooleanProperty(this, "isRequired", isRequired);
this.name.addListener((observable, oldValue, newValue) -> System.out.println(newValue));
}
public StringProperty nameProperty() {return name;}
public final String getName(){return name.get();}
public final void setName(String value){
name.set(value);}
public boolean isRequired() {
return isRequired.get();
}
public BooleanProperty isRequiredProperty() {
return isRequired;
}
public void setRequired(final boolean required) {
this.isRequired.set(required);
}
public ObservableBooleanValue effectiveRequiredProperty() {
// Bindings with this work:
// return isRequired;
// with this not
return isRequired.or(name.isEqualTo(SPECIAL_STRING));
}
}
}
When typing an X into the name the checkbox in the row should be checked.
When typing an X into the name the checkbox in the row is not checked. It's never checked like it is not bound at all.
CheckBoxXXCells don't live up to their doc when it comes to binding their selected state, f.i. (citing here just for signature, even if not set explicitely):
public final Callback <Integer,​ObservableValue<Boolean>> getSelectedStateCallback()
Returns the Callback that is bound to by the CheckBox shown on screen.
clearly talks about an ObservableValue, so we would expect that it at least shows the selection state.
Actually, the implementation does exactly nothing if it's not a property, the relevant part from its updateItem:
StringConverter<T> c = getConverter();
if (showLabel) {
setText(c.toString(item));
}
setGraphic(checkBox);
if (booleanProperty instanceof BooleanProperty) {
checkBox.selectedProperty().unbindBidirectional((BooleanProperty)booleanProperty);
}
ObservableValue<?> obsValue = getSelectedProperty();
if (obsValue instanceof BooleanProperty) {
booleanProperty = (ObservableValue<Boolean>) obsValue;
checkBox.selectedProperty().bindBidirectional((BooleanProperty)booleanProperty);
}
checkBox.disableProperty().bind(Bindings.not(
getTableView().editableProperty().and(
getTableColumn().editableProperty()).and(
editableProperty())
));
To work around, use a custom cell that updates the selected state in its updateItem. With the added quirk that we need to disable the check's firing to really keep the visuals in sync with backing state:
requiredColumn.setCellFactory(cc -> {
TableCell<ViewModel, Boolean> cell = new TableCell<>() {
CheckBox check = new CheckBox() {
#Override
public void fire() {
// do nothing - visualizing read-only property
// could do better, like actually changing the table's
// selection
}
};
{
getStyleClass().add("check-box-table-cell");
check.setOnAction(e -> {
e.consume();
});
}
#Override
protected void updateItem(Boolean item, boolean empty) {
super.updateItem(item, empty);
if (empty || item == null) {
setText(null);
setGraphic(null);
} else {
check.setSelected(item);
setGraphic(check);
}
}
};
return cell;
});

Tableview item dependencies javafx

Good evening,
I am working on a project and I'm at lost at how to configure my tableviews to depend on each other. I would like for the parts in one tableview to depend on the products in the other. How would I go about doing that? I prefer to not use sql at the moment just to keep everything simple as possible. The code snippet is listed below:
public class maincontroller {
private ObservableList<Part> parts = FXCollections.observableArrayList();
private ObservableList<Product> products = FXCollections.observableArrayList();
//code to swap between controllers
#Override
public void initialize(URL url, ResourceBundle rb) {
partsID.setCellValueFactory(new PropertyValueFactory<>("partID"));
partsName.setCellValueFactory(new PropertyValueFactory<>("name"));
partsinvlevel.setCellValueFactory(new PropertyValueFactory<>("instock"));
partscost.setCellValueFactory(new PropertyValueFactory<>("price"));
parttable.setItems(parts);
productsID.setCellValueFactory(new PropertyValueFactory<>("productID"));
productsName.setCellValueFactory(new PropertyValueFactory<>("name"));
productsInvlevel.setCellValueFactory(new PropertyValueFactory<>("instock"));
productsprice.setCellValueFactory(new PropertyValueFactory<>("price"));
producttable.setItems(products);
parttable.getSelectionModel().selectedItemProperty().addListener((ObservableValue<? extends Part> observable, Part oldValue, Part newValue) -> {
});
producttable.getSelectionModel().selectedItemProperty().addListener((ObservableValue<? extends Product> observable, Product oldValue, Product newValue) -> {
});
}
}
I'm not too sure about the relationship between Part and Product. From the context, I would assume each specific Product would have its own list of Parts that is related to it. I would also assume that that list of Parts is stored within Product.
Product:
public class Product
{
private final ObservableList<Part> parts = FXCollections.observableArrayList();
public final ObservableList<Part> getParts() { return parts; }
........
}
This is what you can do:
producttable.getSelectionModel().selectedItemProperty().addListener(observable, oldProduct, newProduct) ->
{
if (newProduct != null)
{
parts.clear();
parts.addAll(newProduct.getParts());
}
});

Checkbox selected bind with TableView

How to bind with TableView selection model with checkbox isSelected in table view.In manually (using mouse multiple select) select the item in tableview
ObservableList<T> observableList = tableView.getSelectionModel().getSelectedItems();
System.out.println(observableList);
how to bind checkbox with tableview
box.selectedProperty().addListener(new ChangeListener<Boolean>() {
#Override
public void changed(ObservableValue<? extends Boolean> ov, Boolean t, Boolean t1) {
if (t1) {
Object object = getTableView().getItems().get(getTableRow().getIndex());
objects.add(object);
getTableView().getItems().set(getTableRow().getIndex(), object);
tableView.getSelectionModel().getSelectedItem(); //tableView.getSelectionModel().setSelectedIndex(getTableRow().getIndex());
} else {
Object object = getTableView().getItems().get(getTableRow().getIndex());
objects.remove(object);
}
System.out.println(objects);
}
});
if Checkbox is selected in row that row item bind with getSelectionModel().getSelectedItems() or how set manually setSelectedItems in tableView
Add to this line in box.selectedProperty().addListener()
tableView.getSelectionModel().select(getTableRow().getIndex());
box.selectedProperty().addListener(new ChangeListener<Boolean>() {
#Override
public void changed(ObservableValue<? extends Boolean> ov, Boolean t, Boolean t1) {
if (t1) {
tableView.getSelectionModel().select(getTableRow().getIndex());
} else {
tableView.getSelectionModel().clearSelection(getTableRow().getIndex());
}
}
});

Custom Java-fx cellfactory messes up the setCellValueFactory

After messing around with Netbeans and Scenebuilder for a while I'm stuck at a problem I can't quite understand. I use a custom cellfactory to bind a doubleclick event to the cells in my tableview. But when I set the cellfactory and a cellValueFactory only the custom cellFactory has an effect.
I'm trying to populate a tableview with data from a number of objects and bind a double click event to the cells of the first column. Populating is not the problem, I just used
idNumber.setCellValueFactory(new PropertyValueFactory<LiveStock, String>("idNumber"));
status.setCellValueFactory(new PropertyValueFactory<LiveStock, String>("status"));
I then googled around to figure out how to bind a doubleclick event to the cells of the table and found javafx, TableView: detect a doubleclick on a cell
amongst others...
I defined a custom cellFactory like this:
Callback<TableColumn<LiveStock, String>, TableCell<LiveStock, String>> cellFactory =
new Callback<TableColumn<LiveStock, String>, TableCell<LiveStock, String>>() {
#Override
public TableCell call(TableColumn p) {
TableCell cell = new TableCell<LiveStock, String>() {};
cell.addEventFilter(MouseEvent.MOUSE_CLICKED, new EventHandler<MouseEvent>() {
#Override
public void handle(MouseEvent event) {
if (event.getClickCount() == 2) {
System.out.println("double clicked!");
TableCell c = (TableCell) event.getSource();
System.out.println("Livestock ID: " + c.getId());
}
}
});
return cell;
}
I removed the update and toString methods just to see if they where the reason I ran in to problems.
So I tried
idNumber.setCellFactory(cellFactory);
idNumber.setCellValueFactory(new PropertyValueFactory<LiveStock, String>("idNumber"));
This results in my cells beeing empty, but having the double click binding
any ideas?
My LiveStock class looks like this:
package projekt1.fx;
import javafx.beans.property.SimpleStringProperty;
public class LiveStock {
private final int idNumber;
private final SimpleStringProperty ownerID;
private SimpleStringProperty status;
private double lat;
private double longd;
public LiveStock(int idNumber, String ownerID) {
this.idNumber = idNumber;
this.ownerID = new SimpleStringProperty(ownerID);
this.status = new SimpleStringProperty("ok");
}
public int getIdNumber() {
return this.idNumber;
}
// public void setIdNumber(int number) {
// this.idNumber = number;
// }
public String getOwnerID(){
return ownerID.get();
}
public void setOwnerID(String id){
ownerID.set(id);
}
public String getStatus(){
return status.get();
}
public void setStatus(String st){
status.set(st);
}
}
The cellfactory now looks like this:
Callback<TableColumn<LiveStock, String>, TableCell<LiveStock, String>> cellFactory =
new Callback<TableColumn<LiveStock, String>, TableCell<LiveStock, String>>() {
#Override
public TableCell call(TableColumn p) {
TableCell cell = new TableCell<LiveStock, String>() {
#Override
public void updateItem(String item, boolean empty) {
super.updateItem(item, empty);
// setText("HELLO WORLD!");
setText(empty ? null : getString());
}
};
cell.addEventFilter(MouseEvent.MOUSE_CLICKED, new EventHandler<MouseEvent>() {
#Override
public void handle(MouseEvent event) {
if (event.getClickCount() == 2) {
System.out.println("double clicked!");
TableCell c = (TableCell) event.getSource();
System.out.println("Livestock ID: " + c.getId());
togglePopup(null);
}
}
});
return cell;
}
};
Documentation of Cell API says:
Because by far the most common use case for cells is to show text to a
user, this use case is specially optimized for within Cell. This is
done by Cell extending from Labeled. This means that subclasses of
Cell need only set the text property, rather than create a separate
Label and set that within the Cell. ...
The current source code of Cell constructor sets the text to null:
public Cell() {
setText(null);
...
}
The subclass IndexedCell and sub-subclass TableCell, both of them don't set the text of Labeled.
The text is set by default cell factory of TableColumn in source code.
public static final Callback<TableColumn<?,?>, TableCell<?,?>> DEFAULT_CELL_FACTORY = new Callback<TableColumn<?,?>, TableCell<?,?>>() {
#Override public TableCell<?,?> call(TableColumn<?,?> param) {
return new TableCell() {
#Override protected void updateItem(Object item, boolean empty) {
if (item == getItem()) return;
super.updateItem(item, empty);
if (item == null) {
super.setText(null);
super.setGraphic(null);
} else if (item instanceof Node) {
super.setText(null);
super.setGraphic((Node)item);
} else {
super.setText(item.toString());
super.setGraphic(null);
}
}
};
}
};
However by defining your own cell factory that creates new TableCell but does not set the text in its overriden updateItem() method, will be resulting an empty (=null) column cell text. So yes the reason of the problem was removing updateItem method, which calls setText(...) internally.
EDIT:
Specify the generic types explicitly for TableColumns as,
TableColumn<LiveStock, Integer> idNumber = new TableColumn<LiveStock, Integer>("ID No");
This will avoid type mismatches or wrong type castings.
Then the cell factory callback for your use case will be
Callback<TableColumn<LiveStock, Integer>, TableCell<LiveStock, Integer>> cellFactory =
new Callback<TableColumn<LiveStock, Integer>, TableCell<LiveStock, Integer>>() {
public TableCell<LiveStock, Integer> call(TableColumn<LiveStock, Integer> p) {
TableCell<LiveStock, Integer> cell = new TableCell<LiveStock, Integer>() {
#Override
public void updateItem(Integer item, boolean empty) {
super.updateItem(item, empty);
setText((item == null || empty) ? null : item.toString());
setGraphic(null);
}
};
cell.addEventFilter(MouseEvent.MOUSE_CLICKED, new EventHandler<MouseEvent>() {
#Override
public void handle(MouseEvent event) {
if (event.getClickCount() > 1) {
System.out.println("double clicked!");
TableCell c = (TableCell) event.getSource();
System.out.println("Cell text: " + c.getText());
}
}
});
return cell;
}
};
What is changed?
The type of idNumber in LiveStock is int. By defining new TableColumn<LiveStock, Integer> we say this is a column from LiveStock row for its attribute idNumber which has a type int, but the generic types must be a reference type, it cannot be TableCell<LiveStock, int> so we define TableCell<LiveStock, Integer>. The thumb of rule: row item class's attribute type should match the second generic type parameter of TableColumn and due to this the parameter of TableCell also.
getString method is defined in the referenced answer link mentioned by you. But it is just a user defined method, not mandatory to use it.

Resources