How to detect a tableview items count resize in JavaFx? - java

I am trying to filter and show items count with FX Tableview.
There are some buttons and txt field for listing a spesific data
here is
text input: filtering
clear button: gettingenter code here default values
this month button: getting this month values
data picker: for getting any day values
There is a label for showing table items size
public Label lblRecordSize;
My problem;
i don't want to lblRecordSize.settext() under the every action for filtering.
Is there a listener like tableview.onItemSizeChangeListener() etc.
UPDATED:
public class CenterDbController implements Initializable {
public AnchorPane centerDbPanelPane;
/** */
public TextField txtSerialNo;
public ComboBox<EModemModel> cbxModemModel;
public Label lblRecordSize;
public DatePicker dpStartDate;
public DatePicker dpEndDate;
public Button btnShow;
public Button btnClear;
/** */
public TableView<RecordedTest> tblvRecords;
public TableColumn colRecordId;
public TableColumn colOfficeId;
public TableColumn colCompany;
public TableColumn colModemSerialNumber;
public TableColumn colModemBrand;
public TableColumn colModemModel;
public TableColumn colSoftwareVersion;
public TableColumn colAccessTest;
public TableColumn colSoftwareTest;
public TableColumn colDhcpTest;
public TableColumn colWifiTest;
public TableColumn colInternetTest;
public TableColumn colResetTest;
public TableColumn colTestResult;
public TableColumn colSendStatus;
public TableColumn colRecordDate;
public TableColumn colTestDetails;
/** */
private IRecordedTestService recordedTestService;
private FilteredList<RecordedTest> filteredList;
#Override
public void initialize(URL location, ResourceBundle resources) {
initCellFactories();
recordedTestService = UtilsForSpring.getSingleBeanOfType(IRecordedTestService.class);
filteredList = new FilteredList<>(FXCollections.observableList(recordedTestService.getThisMonthRecords()), s -> true);
tblvRecords.setItems(filteredList);
lblRecordSize.textProperty().bind(Bindings.size(tblvRecords.getItems()).asString("%s"));
// lblRecordSize.setText(filteredList.size() + "");
filteredList.addListener(new ListChangeListener<RecordedTest>() {
#Override
public void onChanged(Change<? extends RecordedTest> c) {
// lblRecordSize.setText(filteredList.size() + "");
}
});
cbxModemModel.getItems().addAll(EModemModel.values());
}
private void initCellFactories() {
colRecordId.setCellValueFactory(new PropertyValueFactory<RecordedTest, String>("recordId"));
colOfficeId.setCellValueFactory(new PropertyValueFactory<>("officeId"));
colCompany.setCellValueFactory(new PropertyValueFactory<>("company"));
colModemSerialNumber.setCellValueFactory(new PropertyValueFactory<>("modemSerialNumber"));
colModemBrand.setCellValueFactory(new PropertyValueFactory<>("modemBrand"));
colModemModel.setCellValueFactory(new PropertyValueFactory<>("modemModel"));
colSoftwareVersion.setCellValueFactory(new PropertyValueFactory<>("softwareVersion"));
colAccessTest.setCellValueFactory(new PropertyValueFactory<>("accessTest"));
colSoftwareTest.setCellValueFactory(new PropertyValueFactory<>("softwareTest"));
colDhcpTest.setCellValueFactory(new PropertyValueFactory<>("dhcpTest"));
colWifiTest.setCellValueFactory(new PropertyValueFactory<>("wifiTest"));
colInternetTest.setCellValueFactory(new PropertyValueFactory<>("internetTest"));
colResetTest.setCellValueFactory(new PropertyValueFactory<>("resetTest"));
colTestResult.setCellValueFactory(new PropertyValueFactory<>("testResult"));
colSendStatus.setCellValueFactory(new PropertyValueFactory<>("sendStatus"));
colRecordDate.setCellValueFactory(new PropertyValueFactory<>("recordDate"));
colTestDetails.setCellValueFactory(new PropertyValueFactory<>("testDetails"));
}
public void btnClearOnClickAction(ActionEvent e) {
txtSerialNo.clear();
cbxModemModel.getSelectionModel().clearSelection();
dpEndDate.setValue(null);
dpStartDate.setValue(null);
filteredList = new FilteredList<>(FXCollections.observableList(recordedTestService.getThisMonthRecords()), s -> true);
tblvRecords.setItems(filteredList);
// lblRecordSize.setText(filteredList.size() + "");
}
public void btnShowOnClickAction(ActionEvent e) {
if (dpStartDate.getValue() != null && dpEndDate != null) {
filteredList = new FilteredList<>(FXCollections.observableList(recordedTestService.getBetweenRecords(dpStartDate.getValue(), dpEndDate.getValue())));
tblvRecords.setItems(filteredList);
}
}
public void tableOnSortListener() {
// lblRecordSize.setText(tblvRecords.getItems().size() + "");
}
public void txtSerialNoOnKeyPress() {
txtSerialNo.textProperty().addListener(observable -> {
String filter = txtSerialNo.getText();
if (filter == null || filter.length() == 0) {
filteredList.setPredicate(s -> true);
} else {
filteredList.setPredicate(s -> s.getModemSerialNumber().contains(filter));
}
});
}
public void cbxModemModelOnValueChange() {
String filter = cbxModemModel.getSelectionModel().getSelectedItem().toString();
if (filter == null || filter.length() == 0) {
filteredList.setPredicate(s -> true);
} else {
filteredList.setPredicate(s -> s.getModemModel().equalsIgnoreCase(filter));
}
}
}

The Bindings class provides a size method that allows you to create a binding for the size of a ObservableList. Assuming you modify the existing list and do not replace it with a new one every time you filter (e.g. using FilteredList), you can use this to bind the Label text:
// after assigning the items
lblRecordSize.textProperty().bind(Bindings.size(tableview.getItems()).asString("Record count: %s"));
Edit
In your code you replace the items list. The prequesite of the items not being replaced is not given...
You could add a listener to the item property instead and rebind the Label text every time
// before setting items the first time
tblvRecords.itemsProperty().addListener((observable, oldItems, newItems) ->
lblRecordSize.textProperty().bind(
Bindings.size(newItems).asString()));
However you could also modify a single list to contain the source data instead of recreating the lists every time:
private final ObservableList<RecordedTest> data = FXCollections.observableArrayList();
private final FilteredList<RecordedTest> filteredList = new FilteredList<>(data);
#Override
public void initialize(URL location, ResourceBundle resources) {
initCellFactories();
recordedTestService = UtilsForSpring.getSingleBeanOfType(IRecordedTestService.class);
filteredList.setPredicate(null);
data.setAll(recordedTestService.getThisMonthRecords());
tblvRecords.setItems(filteredList);
lblRecordSize.textProperty().bind(Bindings.size(filteredList).asString());
...
}
...
public void btnClearOnClickAction(ActionEvent e) {
...
filteredList.setPredicate(null);
data.setAll(recordedTestService.getThisMonthRecords()));
}

Related

JavaFX - How to disable TreeTableView column or Cell children Node on Button Click

I have JFXTreeTableView which consist of 5 columnsx In that first 2 columns have Delete & Edit Buttons for each cell. After populating table
I want first columns should disable on save Button click.
If above case is not possible then delete Buttons inside first column's cells should be disabled on Save button click.
I did like this but dont know how to disable column or buttons inside cells.
Controller Class
public class FinanceActionsController implements Initializable {
#FXML
private JFXTreeTableView<InvoiceItems> tblInvoiceItemsView;
private JFXButton btnSave;
#FXML
private HBox hbBottonBtnBar;
ObservableList<InvoiceItems> invoiceItems = FXCollections.observableArrayList();
/**
* Initializes the controller class.
*/
#Override
public void initialize(URL url, ResourceBundle rb) {
tableStructure();
btnSave.setOnAction((ActionEvent event) -> {
if (invoiceItems.isEmpty()) {
Alert alert = new Alert(Alert.AlertType.ERROR);
alert.setHeaderText("Please add Atleast one Invoice Item");
alert.showAndWait();
} else {
onClickBtnSaveInvoice();
disableAndAddControlsOnSave();
//tblInvoiceItemsView.setDisable(true);
}
});
}
private void tableStructure() {
JFXTreeTableColumn<InvoiceItems, Boolean> delItem = new JFXTreeTableColumn<>("Delete");
JFXTreeTableColumn<InvoiceItems, String> editItem = new JFXTreeTableColumn<>("Edit");
JFXTreeTableColumn<InvoiceItems, String> billItem = new JFXTreeTableColumn<>("Billable Head");
delItem.setCellValueFactory((TreeTableColumn.CellDataFeatures<InvoiceItems, Boolean> param) -> param.getValue().getValue().getBtnFlag());
delItem.setCellFactory(new Callback<TreeTableColumn<InvoiceItems, Boolean>, TreeTableCell<InvoiceItems, Boolean>>() {
#Override
public TreeTableCell<InvoiceItems, Boolean> call(TreeTableColumn<InvoiceItems, Boolean> param) {
final TreeTableCell<InvoiceItems, Boolean> cell = new TreeTableCell<InvoiceItems, Boolean>() {
MaterialIconView del = new MaterialIconView(MaterialIcon.DELETE_FOREVER, "1.5em");
final JFXButton btnDel = new JFXButton("", del);
#Override
public void updateItem(Boolean item, boolean empty) {
super.updateItem(item, empty);
if (empty) {
setGraphic(null);
setText(null);
} else {
btnDel.disableProperty().bind(txtN.disableProperty());
del.setFill(Color.RED);
btnDel.setButtonType(JFXButton.ButtonType.RAISED);
btnDel.setOnAction(event -> {
});
setGraphic(btnDel);
setText(null);
}
}
};
return cell;
}
});
billItem.setCellValueFactory((TreeTableColumn.CellDataFeatures<InvoiceItems, String> param) -> param.getValue().getValue().getBillItemDesc());
final TreeItem<InvoiceItems> root = new RecursiveTreeItem<>(invoiceItems, RecursiveTreeObject::getChildren);
tblInvoiceItemsView.getColumns().setAll(delItem, editItem, billItem);
tblInvoiceItemsView.setRoot(root);
tblInvoiceItemsView.setShowRoot(false);
}
Class InvoiceItems -
class InvoiceItems extends RecursiveTreeObject<InvoiceItems> {
StringProperty billItemDesc;
BooleanProperty btnFlag;
public InvoiceItems(String billItemDesc) {
this.billItemDesc = new SimpleStringProperty(billItemDesc);
}
public StringProperty getBillItemDesc() {
return billItemDesc;
}
public BooleanProperty getBtnFlag() {
return btnFlag;
}
public void setBtnFlag(Boolean btnFlag) {
this.btnFlag = new SimpleBooleanProperty(btnFlag);
}
}
I have tried to pass InvoiceItems setBtnFlag as True in Observable list to work in setCellFactory's updateItem method but not working. Please help any help will be appreciable, Thank You.

Create a listener logic for a custom combobox

I am trying to create a listener logic for a custom combo box that I have created that contains items with check boxes.
I was not able to proceed as I am not getting an idea on how to do it.
MainApplication.java
public class MainApplication extends Application{
#Override
public void start(Stage stage) {
Scene scene = new Scene(new VBox(), 450, 250);
ComboBox<ComboBoxItemWrap<Person>> cb = new ComboBox<>();
#SuppressWarnings("unchecked")
ObservableList<ComboBoxItemWrap<Person>> options = FXCollections.observableArrayList(
new ComboBoxItemWrap<>(new Person("A", "12-Aug-1994")),
new ComboBoxItemWrap<>(new Person("B", "13-Aug-1994")),
new ComboBoxItemWrap<>(new Person("C", "14-Aug-1994"))
);
cb.setCellFactory( c -> {
ListCell<ComboBoxItemWrap<Person>> cell = new ListCell<ComboBoxItemWrap<Person>>(){
#Override
protected void updateItem(ComboBoxItemWrap<Person> item, boolean empty) {
super.updateItem(item, empty);
if (!empty) {
final CheckBox cb = new CheckBox(item.toString());
cb.selectedProperty().bind(item.checkProperty());
setGraphic(cb);
}
}
};
cell.addEventFilter(MouseEvent.MOUSE_RELEASED, event -> {
cell.getItem().checkProperty().set(!cell.getItem().checkProperty().get());
StringBuilder sb = new StringBuilder();
cb.getItems().filtered( f-> f!=null).filtered( f-> f.getCheck()).forEach( p -> {
sb.append("; "+p.getItem());
});
final String string = sb.toString();
cb.setPromptText(string.substring(Integer.min(2, string.length())));
});
return cell;
});
cb.setItems(options);
VBox root = (VBox) scene.getRoot();
Button bt = new Button("test");
bt.setOnAction(event -> {
cb.getItems().filtered( f -> f.getCheck()).forEach( item -> System.out.println(item.getItem()));
});
root.getChildren().addAll(cb, bt);
stage.setScene(scene);
stage.show();
}
public static void main(String[] args) {
launch(args);
}
}
ComboBoxItemWrap.java
public class ComboBoxItemWrap<T> {
private BooleanProperty check = new SimpleBooleanProperty(false);
private ObjectProperty<T> item = new SimpleObjectProperty<>();
ComboBoxItemWrap() {
}
ComboBoxItemWrap(T item) {
this.item.set(item);
}
ComboBoxItemWrap(T item, Boolean check) {
this.item.set(item);
this.check.set(check);
}
public BooleanProperty checkProperty() {
return check;
}
public Boolean getCheck() {
return check.getValue();
}
public void setCheck(Boolean value) {
check.set(value);
}
public ObjectProperty<T> itemProperty() {
return item;
}
public T getItem() {
return item.getValue();
}
public void setItem(T value) {
item.setValue(value);
}
#Override
public String toString() {
return item.getValue().toString();
}
}
Person.java
public class Person {
private StringProperty name = new SimpleStringProperty();
private StringProperty birthday = new SimpleStringProperty();
public Person() {
}
public Person(String name, String birthday) {
setNameValue(name);
setBirthdayValue(birthday);
}
public StringProperty getNameProperty() {
return name;
}
public String getNameValue() {
return name.getValue();
}
public void setNameValue(String value) {
name.setValue(value);
}
public StringProperty getBirthdayProperty() {
return birthday;
}
public String getBirthdayValue() {
return birthday.getValue();
}
public void setBirthdayValue(String value) {
birthday.setValue(value);
}
#Override
public String toString() {
return getNameValue()+" ("+getBirthdayValue()+")";
}
}
In the output application, a list of items with check boxes will get populated. On selection of any number of entries in the list, the entry name gets populated on the combo box itself separated by a ';'. Now I want my back end code to listen and identify the entries that have been selected in order to perform further operations.
You may not need to reinvent the wheel. Consider using ControlsFX CheckComboBox.
That being said there are several problems in the code:
You never update the property on a selection of the CheckBox. This can be easily fixed by using bidirectional bindings.
Since the ComboBox popup is closed, the CheckBox is no longer armed at the time the MOUSE_RELEASED event is triggered. this is a prerequesite for the selected state of the CheckBox changing though. Modifying the skin allows you to change this behaviour.
You use ObservableList.filtered to create FilteredLists that you throw away immediately afterwards. You also create a filtered list of a filtered list in the MOUSE_RELEASED event filter. This is not wrong per se, but you're creating an expensive object there without the need to do so: simply get a stream there. This is a much more lightweight way to filter a list, if the result is only needed once. Use filtered/FilteredList only if you need an ObservableList that contains elements from another ObservableList and that is automatically updated.
Also note that there is a way to make an ObservableList trigger update changes on a change of a property: Use the observableArrayList method taking an extractor as parameter.
This is how you could rewrite your code to make it work:
VBox root = new VBox();
Scene scene = new Scene(root, 450, 250);
ComboBox<ComboBoxItemWrap<Person>> cb = new ComboBox<>();
ObservableList<ComboBoxItemWrap<Person>> options = FXCollections.observableArrayList(item -> new Observable[] {item.checkProperty()});
options.addAll(
new ComboBoxItemWrap<>(new Person("A", "12-Aug-1994")),
new ComboBoxItemWrap<>(new Person("B", "13-Aug-1994")),
new ComboBoxItemWrap<>(new Person("C", "14-Aug-1994")));
cb.setCellFactory(c -> new ListCell<ComboBoxItemWrap<Person>>() {
private final CheckBox cb = new CheckBox();
#Override
protected void updateItem(ComboBoxItemWrap<Person> item, boolean empty) {
ComboBoxItemWrap<Person> oldItem = getItem();
if (oldItem != null) {
// remove old binding
cb.selectedProperty().unbindBidirectional(oldItem.checkProperty());
}
super.updateItem(item, empty);
if (empty || item == null) {
setGraphic(null);
} else {
cb.selectedProperty().bindBidirectional(item.checkProperty());
cb.setText(item.toString());
setGraphic(cb);
}
}
});
// make sure popup remains open
ComboBoxListViewSkin<ComboBoxItemWrap<Person>> skin = new ComboBoxListViewSkin<>(cb);
skin.setHideOnClick(false);
cb.setSkin(skin);
cb.setItems(options);
cb.promptTextProperty().bind(Bindings.createStringBinding(() ->
options.stream().filter(ComboBoxItemWrap::getCheck).map(Object::toString).collect(Collectors.joining("; ")), options));
Note that if you want the popup to be closed after (de)selecting a checkbox, you could simply add a event filter for MOUSE_RELEASED for the checkbox that calls cb.arm() instead of modifying the skin.

JavaFx: TableView - unbind all properties

I have a TableView and the columns for it I create like this:
TableColumn<Foo,String> fieldColumn=new TableColumn("field");
fieldColumn.setCellValueFactory(data->data.getValue().getFieldProperty());
tableView.add(fieldColumn);
Now I want to destroy my tableView but I want to continue to use all foos. That's why I want to unbind foos' properties from table/column. How to do it?
If you remove the TableColumns, the listeners will be removed during the next layout pass. This allows you to remove the listeners added by the TableView by clearing the columns and calling layout():
Item class for allowing to get the number of listeners to the property
public class Item {
private final Set<Object> listeners = new HashSet<>();
public Item(String value) {
this.value.set(value);
}
private final StringProperty value = new SimpleStringProperty() {
#Override
public void removeListener(ChangeListener<? super String> listener) {
super.removeListener(listener);
listeners.remove(listener);
}
#Override
public void addListener(ChangeListener<? super String> listener) {
super.addListener(listener);
listeners.add(listener);
}
#Override
public void removeListener(InvalidationListener listener) {
super.removeListener(listener);
listeners.remove(listener);
}
#Override
public void addListener(InvalidationListener listener) {
super.addListener(listener);
listeners.add(listener);
}
};
public final StringProperty valueProperty() {
return this.value;
}
public int getListenerCount() {
return listeners.size();
}
}
Text Application
private void printListenerCount(String message) {
System.out.println(message + tableView.getItems().stream().mapToInt(Item::getListenerCount).sum());
}
private TableView<Item> tableView;
#Override
public void start(Stage primaryStage) {
tableView = new TableView<>();
tableView.getItems().addAll(new Item("a"), new Item("b"), new Item("c"));
TableColumn<Item, String> column = new TableColumn<>();
column.setCellValueFactory(cd -> cd.getValue().valueProperty());
tableView.getColumns().add(column);
Button btn = new Button("print listener count");
btn.setOnAction((ActionEvent event) -> {
printListenerCount("listeners: ");
});
Button btn2 = new Button("clear columns");
btn2.setOnAction(evt -> {
tableView.getColumns().clear();
// do layout to remove the listeners added for the columns
tableView.layout();
printListenerCount("after clear columns: ");
});
Scene scene = new Scene(new VBox(tableView, btn, btn2));
primaryStage.setScene(scene);
primaryStage.show();
}
Pressing the print listener count and then the clear columns buttons will result in the following output:
listeners: 3
after clear columns: 0

Multi Line editable cell in tableview javafx

Hi I just want multi line cell in JavaFX which is editable.
#FXML
private TableColumn bookName;
#Override
public void initialize(URL url, ResourceBundle rb) {
tableView.setEditable(true);
// setCellFactory();
bookName.setCellFactory(TextFieldTableCell.forTableColumn()); // If I comment this line and uncomment above line then cell will be multiline but not editable
bookName.setOnEditCommit(
new EventHandler<TableColumn.CellEditEvent<Books, String>>() {
#Override
public void handle(TableColumn.CellEditEvent<Books, String> t) {
if (t.getNewValue().equals("") || t.getNewValue().length() < 3) {
info.setText("Book name must be greater than 3 characters.");
((Books) t.getTableView().getItems().get(
t.getTablePosition().getRow())).setBookName(t.getOldValue());
return;
}
((Books) t.getTableView().getItems().get(
t.getTablePosition().getRow())).setBookName(t.getNewValue());
}
});
}
If I set custom cellFactory, cell will be multiline but not editable
private void setCellFactory() {
Callback<TableColumn, TableCell> cellFactory = new Callback<TableColumn, TableCell>() {
#Override
public TableCell call(TableColumn param) {
final TableCell cell = new TableCell() {
private Text text;
#Override
public void updateItem(Object item, boolean empty) {
super.updateItem(item, empty);
if (!isEmpty()) {
text = new Text(item.toString());
text.setWrappingWidth(140);
setGraphic(text);
}
}
};
return cell;
}
};
bookName.setCellFactory(cellFactory);
}
Not editable multiline cell.

ArrayList displayed incorrectly

I'm testing to display Array List into JavaFX accordion:
public class Navigation {
// Object for storing conenctions
public static List<dataObj> list = new ArrayList<>();
private ObservableList<dataObj> data;
public class dataObj {
private String conenctionname;
public dataObj(String conenctionname) {
this.conenctionname = conenctionname;
}
public String getConenctionname() {
return conenctionname;
}
public void setConenctionname(String conenctionname) {
this.conenctionname = conenctionname;
}
}
public void initNavigation(Stage primaryStage, Group root, Scene scene) {
VBox stackedTitledPanes = createStackedTitledPanes();
ScrollPane scroll = makeScrollable(stackedTitledPanes);
scroll.getStyleClass().add("stacked-titled-panes-scroll-pane");
scroll.setPrefSize(395, 580);
scroll.setLayoutX(5);
scroll.setLayoutY(32);
root.getChildren().add(scroll);
}
private ScrollPane makeScrollable(final VBox node) {
final ScrollPane scroll = new ScrollPane();
scroll.setContent(node);
scroll.viewportBoundsProperty().addListener(new ChangeListener<Bounds>() {
#Override
public void changed(ObservableValue<? extends Bounds> ov, Bounds oldBounds, Bounds bounds) {
node.setPrefWidth(bounds.getWidth());
}
});
return scroll;
}
/////////////////////////////////////////////////////////////////////////////////////
// Generate accordition with Connections, Tables and Description
private VBox createStackedTitledPanes() {
VBox stackedTitledPanes = new VBox();
stackedTitledPanes.getChildren().setAll(
createConnectionsList("Connections"));
((TitledPane) stackedTitledPanes.getChildren().get(0)).setExpanded(true);
stackedTitledPanes.getStyleClass().add("stacked-titled-panes");
return stackedTitledPanes;
}
//////////////////////////////////////////////////////////////////////////////
// Generate list with Connections
public TitledPane createConnectionsList(String title) {
initObject();
data = FXCollections.observableArrayList(list);
ListView<dataObj> lv = new ListView<>(data);
lv.setCellFactory(new Callback<ListView<dataObj>, ListCell<dataObj>>() {
#Override
public ListCell<dataObj> call(ListView<dataObj> p) {
return new ConnectionsCellFactory();
}
});
AnchorPane content = new AnchorPane();
content.getChildren().add(lv);
// add to TitelPane
TitledPane pane = new TitledPane(title, content);
return pane;
}
static class ConnectionsCellFactory extends ListCell<dataObj> {
#Override
public void updateItem(dataObj item, boolean empty) {
super.updateItem(item, empty);
if (item != null) {
for (int i = 0; i < list.size(); i++) {
setText(list.get(i).getConenctionname());
}
}
}
}
// Insert Some test data
public void initObject() {
dataObj test1 = new dataObj("test data 1");
dataObj test2 = new dataObj("test data 2");
list.add(test1);
list.add(test2);
}
}
But for some reason I cannot get proper list of Objects from the Array list and display them. I get this result:
The proper result should be test data 1 and test data 2. How I can fix this?
The problem is in the ConnectionsCellFactory, the method updateItem is called for every item in List. So in you code, for every cell you are setting the text for every item in the list
you should try:
static class ConnectionsCellFactory extends ListCell<dataObj> {
#Override
public void updateItem(dataObj item, boolean empty) {
super.updateItem(item, empty);
if (item != null) {
setText(item.getConenctionname());
}
}
}

Categories