JavaFX TableView custom row style on double click - css

I have a TableView in my JavaFX application.
I would like to style differently row when it is double-clicked on it, and differently when it is single-clicked.
Here is what I achieve:
final PseudoClass doubleClickPseudoClass = PseudoClass.getPseudoClass("new");
setRowFactory(tableView -> {
final TableRow<Bean> row = new TableRow<Bean>();
row.setOnMouseClicked(event -> {
if (event.getClickCount() == 2 && (! row.isEmpty())) {
row.pseudoClassStateChanged(doubleClickPseudoClass, true);
});
return row;
});
However, when the user doubles click on every new row, I want all previously double-clicked rows to be styled without applying "new" class:
row.pseudoClassStateChanged(doubleClickPseudoClass, false);
How can I do that?
Now I have cumulative styled all rows as they are double-clicked.

You shouldn't use TableRows to store the state themselves since new items may be assigned to a TableRow instance. Instead use a property to store the item double-clicked item and use a listener for styling the rows:
final ObjectProperty<Bean> doubleClickedObject = new SimpleObjectProperty<>();
setRowFactory(tableView -> new TableRow<Bean>() {
private void updateStyle() {
pseudoClassStateChanged(doubleClickPseudoClass, !isEmpty() && doubleClickedObject.get() == getItem());
}
private final InvalidationListener listener;
{
listener = o -> updateStyle();
doubleClickedObject.addListener(new WeakInvalidationListener(listener));
setOnMouseClicked(event -> {
if (!isEmpty() && event.getClickCount() == 2) {
doubleClickedObject.set(getItem());
}
});
}
#Override
protected void updateItem(Bean item, boolean empty) {
super.updateItem(item, empty);
updateStyle();
}
});

Related

Add buttons to the row currently being edited

I have tried searching both google and stackoverflow for answer to my question but I could not find any.
I have a program that adds/reads data from a database and to a tableview. I want to be able to edit the previously entered data from the tableview by adding two buttons (Save and abort) to the current row being edited.
I am having some troubles understanding the routines like Callback etc. But I have managed to get a column with two buttons to appear when I start the edit. But I get buttons on every column I just want the buttons on the currently
selected row. Also I don't really understand how to get the current object from the save-button to be able to save it.
And also how the abort-button should work to cancel all changes.
public void setUpTableView() {
columnAnkomstdatum.setCellValueFactory(new PropertyValueFactory<>("arrivalDate"));
columnSupplier.setCellValueFactory(new PropertyValueFactory<>("supplier"));
columnRadiopharmaceutical.setCellValueFactory(new PropertyValueFactory<>("radiopharmaceutical"));
columnActivity.setCellValueFactory(new PropertyValueFactory<>("startActivity"));
columnCalibrationdate.setCellValueFactory(new PropertyValueFactory<>("startDate"));
columnBatchNumber.setCellValueFactory(new PropertyValueFactory<>("batchNumber"));
columnContaminationControl.setCellValueFactory(new PropertyValueFactory<>("contaminationControll"));
columnRoom.setCellValueFactory(new PropertyValueFactory<>("room"));
columnUser.setCellValueFactory(new PropertyValueFactory<>("user"));
tableview.setEditable(true);
columnSupplier.setEditable(true);
columnSupplier.setCellFactory(ComboBoxTableCell.forTableColumn(supplierList));
columnSupplier.setOnEditCommit(t -> {
ArrayList<Radiopharmaceutical> radioListfromSupplier = new RadiopharmaceuticalDao().getRadiopharmaceuticalsBySupplierName(t.getNewValue().getSupplierName());
radioList = FXCollections.observableArrayList(radioListfromSupplier);
t.getRowValue().setSupplier(t.getNewValue());
columnRadiopharmaceutical.setCellFactory(ComboBoxTableCell.forTableColumn(radioList));
if(tableview.getColumns().size() <= 9) {
addButtonsToTable();
}
});
}
private void addButtonsToTable() {
TableColumn<RegRadio, Void> editRow = new TableColumn<>("Edit");
tableview.getColumns().add(editRow);
Callback<TableColumn<RegRadio, Void>, TableCell<RegRadio, Void>> cellFactory = new Callback<TableColumn<RegRadio,Void>, TableCell<RegRadio,Void>>() {
#Override
public TableCell<RegRadio, Void> call(final TableColumn<RegRadio, Void> param) {
final TableCell<RegRadio, Void> cell = new TableCell<RegRadio, Void>() {
private final Button btnSave = new Button("Save");
private final Button btnAbort = new Button("Avbryt");
{
btnSave.setOnAction((ActionEvent event) -> {
RegRadio rr = getTableView().getItems().get(getIndex());
System.out.println("Saved");
});
}
{
btnAbort.setOnAction((ActionEvent event) -> {
System.out.println("Abort");
});
}
#Override
public void updateItem(Void item, boolean empty) {
super.updateItem(item, empty);
if (empty) {
setGraphic(null);
} else {
HBox pane = new HBox(btnSave, btnAbort);
setGraphic(pane);
}
}
};
return cell;
}
};
editRow.setCellFactory(cellFactory);
tableview.getColumns().add(editRow);
}
In your updateItem callback you can check if the cell is in the selected row in order to decide if you should show the buttons or not. Additionally you also need a flag to check if the user is editing. Something like this:
#Override
public void updateItem(Void item, boolean empty) {
super.updateItem(item, empty);
var selectedCells = tableview.getSelectionModel().getSelectedCells();
if (empty || !isEditing || selectedCells.isEmpty || getTableRow().getIndex() != selectedCells.get(0).getRow()) {
setGraphic(null);
} else {
HBox pane = new HBox(btnSave, btnAbort);
setGraphic(pane);
}
}
Another approach would be to change the visibility of the buttons depending on whether the cell is in the selected row:
#Override
public void updateItem(Void item, boolean empty) {
super.updateItem(item, empty);
if (empty) {
setGraphic(null);
} else {
HBox pane = new HBox(btnSave, btnAbort);
var selectedCells = tableview.getSelectionModel().getSelectedCells();
pane.setVisible(!selectedCells.isEmpty() && getTableRow().getIndex() == selectedCells.get(0).getRow())
setGraphic(pane);
}
}
(I haven't compiled and tested these code samples)

Cell dependent appearance of cell in JavaFX

How can I change the appearance of a cell based on the status of another cell in the same column of a tableView in JavaFX.
colHidden.setCellValueFactory(param -> {
return param.getValue().hiddenProperty();
});
colHidden.setCellFactory(tc -> new CheckBoxTableCell<>());
colLabel.setCellFactory(...);
I have a cell which contains CheckBoxTableCell<>(). When I select this checkbox the content of the cell colLabel should be replaced by asterisks.
You can do something like this. The basic idea here is to "remember" the property corresponding to the check box in the same row, assuming the cell is not empty, and add a listener to it that updates the text. Then remove the listener from the previous property whenever the cell updates.
colLabel.setCellFactory(col -> new TableCell<RowType, ColumnType>() {
private ObservableValue<Boolean> hiddenProperty ;
ChangeListener<Boolean> listener = (obs, wasHidden, isNowHidden) -> updateText(isNowHidden);
#Override
protected void updateItem(ColumnType item, boolean empty) {
super.updateItem(item, empty);
if (hiddenProperty != null) {
hiddenProperty.removeListener(listener);
}
if (empty) {
setText(null);
hiddenProperty = null ;
} else {
hiddenProperty = getTableView().getItems().get(getIndex()).hiddenProperty();
hiddenProperty.addListener(listener);
updateText(hiddenProperty.get());
}
}
private void updateText(boolean hidden) {
if (hidden) {
setText("********");
} else {
setText(getItem().toString()); // or other format for string, etc
}
}
}
Replace RowType and ColumnType with the actual types used by colLabel (i.e. this assumes you have TableColumn<RowType, ColumnType> colLabel ;).

JavaFX row in table view didn't remove ater remove element

I have a litre problem with table view. When I remove element from observable list, the row correspandont on this element is not deleted.
I have a relations list ( person1, type, person2 => children)
When I add a new child the rellation is created (null, neutral, null => child).
After add some people, I change the relations beetwen them, so, when I indicate the siblings, some realtions are deleted. But it is still visible in table view. It is not selectable, but when I click on, it is indique . the last relation in the list.
When I add new Person, the row are overrided.
this.relationSimLeftColumn.setCellValueFactory(cellData -> cellData.getValue().simLeftProperty());
this.relationSimRightColumn.setCellValueFactory(cellData -> cellData.getValue().simRightProperty());
this.relationTypeColumn.setCellValueFactory(cellData -> cellData.getValue().typeProperty());
and cell factory :
private Callback<TableColumn<GTX_Relation, GTX_Member>,
TableCell<GTX_Relation, GTX_Member>> setMemberCellFactory(String parameter) {
Callback<TableColumn<GTX_Relation, GTX_Member>, TableCell<GTX_Relation, GTX_Member>> callback =
new Callback<TableColumn<GTX_Relation, GTX_Member>, TableCell<GTX_Relation, GTX_Member>>() {
#Override
public TableCell<GTX_Relation, GTX_Member> call(TableColumn<GTX_Relation, GTX_Member> param) {
TableCell<GTX_Relation, GTX_Member> cell = new TableCell<GTX_Relation, GTX_Member>() {
#Override
protected void updateItem(GTX_Member item, boolean empty) {
super.updateItem(item, empty);
ImageView imageview = new ImageView();
if (item != null) {
imageview.setFitHeight(TABLE_IMAGE_MEMBER_HEIGHT);
imageview.setFitWidth(TABLE_IMAGE_MEMBER_WIDTH);
imageview.setImage(new Image(item.getPhoto()));
setGraphic(imageview);
} else {
if (!empty) {
imageview.setFitHeight(TABLE_IMAGE_MEMBER_HEIGHT);
imageview.setFitWidth(TABLE_IMAGE_MEMBER_WIDTH);
String path = parameter.equals("LEFT") == true ?
ImageFiles.NO_NAME_FEMALE.toString() : ImageFiles.NO_NAME_MALE.toString();
imageview.setImage(new Image(path));
setGraphic(imageview);
}
}
}
};
return cell;
}
};
return callback;
}
Your cell factory needs to handle the case where the cell is empty.
When you remove an item from the table's items list, a cell that was previously used to display an item will (potentially) be reused as an empty cell; i.e. its updateItem(null, true) method will be called. Your current implementation doesn't do anything for the case where item == null && empty == true, so the cell's appearance won't be changed.
You need
#Override
protected void updateItem(GTX_Member item, boolean empty) {
super.updateItem(item, empty);
ImageView imageview = new ImageView();
if (item != null) {
imageview.setFitHeight(TABLE_IMAGE_MEMBER_HEIGHT);
imageview.setFitWidth(TABLE_IMAGE_MEMBER_WIDTH);
imageview.setImage(new Image(item.getPhoto()));
setGraphic(imageview);
} else {
if (empty) {
setGraphic(null);
} else {
imageview.setFitHeight(TABLE_IMAGE_MEMBER_HEIGHT);
imageview.setFitWidth(TABLE_IMAGE_MEMBER_WIDTH);
String path = parameter.equals("LEFT") == true ?
ImageFiles.NO_NAME_FEMALE.toString() : ImageFiles.NO_NAME_MALE.toString();
imageview.setImage(new Image(path));
setGraphic(imageview);
}
}
}

Javafx tableview columview with button I need to click button autoselect row

I need to click on the button that is already in a column to automatically select the row to be able to get and set the selected item. Otherwise, generate nullpointer. I was thinking of adding a listener that when I click the button select the row directly, but I do not know.
ScreenShot :
This is the code:
botonVisitar.setOnAction((ActionEvent event) -> {
TextInputDialog dialog1 = new TextInputDialog();
Stage stage1 = (Stage) dialog1.getDialogPane().getScene().getWindow();
stage1.getIcons().add(new Image(this.getClass().getResource("icono.jpg").toString()));
dialog1.setTitle("Visita:");
dialog1.setContentText("Ingresar Tipo de visita: (por ejemplo: llamada, mail, mensaje, etc)");
Optional<String> result1 = dialog1.showAndWait();
if (result1.isPresent()) {
TablaVisita.getSelectionModel().getSelectedItem().setTipoVisita(result1.get());
TablaVisita.getSelectionModel().getSelectedItem().setFechaVisita(LocalDate.now());
//If it is not selected i get nullPointer.
}
I assume you're doing this in a custom TableCell used with a cellFactory, which means you can use the TableRow to get the table item:
new TableCell<MyItem, Void>() {
private final Button botonVisitar = new Button("Visitar");
{
botonVisitar.setOnAction((ActionEvent event) -> {
...
if (result1.isPresent()) {
MyItem item = (MyItem) getTableRow().getItem();
item.setTipoVisita(result1.get());
item.setFechaVisita(LocalDate.now());
}
});
}
#Override
public void updateItem(Void item, boolean empty) {
super.updateItem(item, empty);
setGraphic(empty ? null : botonVisitar);
}
}

How to add multiple listener at treeview in javafx? (hover, focused)

I have made treeview. I want to make treeview like this.
when I enter mouse to item, that item should change image.
when I clcick mouse to item, that item should chnage image.
I know how the way getSelectionMode()... but I don't know hover event.
Please help me.
Not sure if I understand you correct.
But to change your image as soon as you click an image use a selectedItemProperty listener:
treeView.getSelectionModel().selectedItemProperty().addListener( new ChangeListener() {
#Override
public void changed(ObservableValue observable, Object oldValue,
Object newValue) {
TreeItem<String> selectedItem = (TreeItem<String>) newValue;
// do something
}
});
If you want it as soon as you hover over the item you can use a hoverProperty on the row:
treeView.setRowFactory(tableView -> {
final TableRow<Person> row = new TableRow<>();
row.hoverProperty().addListener((observable) -> {
final YourItem yourItem = row.getItem();
if (yourItem.isHover() ) {
// do something
} else {
// do something
}
});
return row;
});
(this code is from the answer here)
I missread, its about a TreeView. You can try an onMouseEntered or similiar within a cellfactory:
treeView.setCellFactory(tv -> {
final Tooltip tooltip = new Tooltip();
TreeCell<Path> cell = new TreeCell<Path>() {
#Override
public void updateItem(Path item, boolean empty) {
super.updateItem(item, empty);
}
};
cell.setOnMouseClicked(e -> {
// do something
});
cell.setOnMouseEntered(e -> {
// do something
});
return cell ;
});

Resources