I have a JavaFX TableView populated with many TableColumns, one of which is matImageColumn. The code for it looks like:
matImageColumn.setCellFactory(new Callback<TableColumn<CustomObject, ImageView>, TableCell<CustomObject, ImageView>>() {
#Override
public TableCell call(final TableColumn<CustomObject, ImageView> param) {
final TableCell<CustomObject, ImageView> cell = new TableCell<CustomObject, ImageView>() {
ImageView img = new ImageView();
#Override
public void updateItem(ImageView item, boolean empty) {
super.updateItem(item, empty);
if (empty) {
setGraphic(null);
setText(null);
} else {
CustomObject co = getTableView().getItems().get(getIndex());
img = co.getImageViewOfMat();
setGraphic(img);
setText(null);
}
}
};
return cell;
}
});
Now as it is, I am looking to include a click event on my imageview but I am clueless from where to start.Can you show me possible solutions to this issue? Thanks in advance.
Related
I have a TableView where the last column is an "Action" column, containing a custom ActionBox object, with buttons for several actions. To add behaviour to this ActionBox, I need to pass a Data object to it. However, I don't know how to reference the object.
class TableViewWithActionColumn() {
#FXML TableColumn<Data, Void> actionColumn;
public TableViewWithActionColumn() {
// Code for loading custom component...
}
#FXML
public void initialize() {
populateActionColumn();
}
private void populateActionColumn() {
Callback<TableColumn<Data, Void>, TableCell<Data, Void>> cellFactory = new Callback<TableColumn<Data, Void>, TableCell<Data, Void>>() {
#Override
public TableCell<Data, Void> call(final TableColumn<Data, Void> param) {
return new TableCell<Data, Void>() {
private final ActionBox actionBox = new ActionBox();
#Override
public void updateItem(Void item, boolean empty) {
super.updateItem(item, empty);
if (empty) {
setGraphic(null);
} else {
setGraphic(actionBox);
}
}
};
}
};
actionColumn.setCellFactory(cellFactory);
}
}
I assumed that the reference to the object was in Void item, so I tried replacing all occurrences of Void with Data, and doing setGraphic(new ActionBox(item));, but this led to a NullPointerException, so I suppose that's not the right way to do it. So, how do I reference row's data in CellFactory context?
TableCell has a getTableRow() method, that gives you a reference to the TableRow that contains the cell. The TableRow is itself a cell implementation, so you can call its getItem() method to get the data represented by the row.
In context (and removing all the unnecessary boilerplate from your code):
private void populateActionColumn() {
actionColumn.setCellFactory(col -> new TableCell<Data, Void>() {
private final ActionBox actionBox = new ActionBox();
#Override
public void updateItem(Void item, boolean empty) {
super.updateItem(item, empty);
if (empty) {
setGraphic(null);
} else {
Data rowData = getTableRow().getItem();
// ... something like: actionBox.setData(rowData) ;
setGraphic(actionBox);
}
}
});
}
I wanted to add a column if the line wasn't empty. In this one I wanted to put buttons with different ImageView to be able to go to an add or view page.
I'm trying to change the icon of a button according to the content of my row in a TableView (here if dateR (string) is null)
I manage to move it to such and such a page depending on the dateR value, but I can't change the imageView...
Thanks
TableColumn<ModelTab4, Void> visualiserAjouter = new TableColumn<ModelTab4, Void>("Visualiser/Ajouter");
Callback<TableColumn<ModelTab4, Void>, TableCell<ModelTab4, Void>> cellFactory1 = (final TableColumn<ModelTab4, Void> param) -> {
final TableCell<ModelTab4, Void> cell = new TableCell<ModelTab4, Void>() {
private String dateR;
private final Button btn1 = new Button();
{
btn1.setOnAction(click -> {
try {
ModelTab4 analyse = getTableView().getItems().get(getIndex());
dateR = analyse.getDateR();
setDate(dateR);
idAnalyse = analyse.getNum1();
if (dateR!=null){
mainApp.goConsultResult(idPerso, idPatient, idAnalyse);
}
else {
mainApp.goAjoutResult(idPerso, idPatient, idAnalyse);
}
} catch (Exception e) {
e.printStackTrace();
}
});
}
public void setDate(String date) {
this.dateR = date;
}
public String getDate() {
return dateR;
}
public void updateItem(Void item, boolean empty) {
super.updateItem(item, empty);
if (empty) {
setGraphic(null);
}
else {
if (getDate()!=null){
setGraphic(btn1);
ImageView icone1 = new ImageView("consult.png");
icone1.setFitHeight(20);
icone1.setFitWidth(20);
btn1.setGraphic(icone1);
setStyle("-fx-alignment : CENTER;");
}
else if (getDate()==null){
setGraphic(btn1);
ImageView icone2 = new ImageView("ajout.png");
icone2.setFitHeight(20);
icone2.setFitWidth(20);
btn1.setGraphic(icone2);
setStyle("-fx-alignment : CENTER;");
}
}
}
};
return cell;
};
Assuming your ModelTab4 class includes the following:
public class ModelTab4 {
private final StringProperty dateR = new SimpleStringProperty() ;
public StringProperty dateRProperty() {
return dateR ;
}
public final String getDateR() {
return dateRProperty().get();
}
public final void setDateR(String dateR) {
dateRProperty().set(dateR);
}
// other properties ...
}
you should make the value in the visualiserAjouter column the dateR property:
TableColumn<ModelTab4, String> visualiserAjouter = new TableColumn<>("Visualiser/Ajouter");
visualiserAjouter.setCellValueFactory(cellData -> cellData.getValue().dateRProperty());
Now the item in the cell implementation is the dateR property. The issue with the code the way you had it, is that the only way the property changes is after the button is pressed, and there is no notification back to the cell that the value has changed. So in updateItem() you're checking the value of dateR before is has had a chance to change. With the setup above, you can now do:
final TableCell<ModelTab4, Void> cell = new TableCell<ModelTab4, Void>() {
private final Button btn1 = new Button();
{
btn1.setOnAction(click -> {
try {
ModelTab4 analyse = getTableView().getItems().get(getIndex());
idAnalyse = analyse.getNum1();
if (getItem()!=null){
mainApp.goConsultResult(idPerso, idPatient, idAnalyse);
}
else {
mainApp.goAjoutResult(idPerso, idPatient, idAnalyse);
}
} catch (Exception e) {
e.printStackTrace();
}
});
}
public void updateItem(String dateR, boolean empty) {
super.updateItem(item, empty);
if (empty) {
setGraphic(null);
}
else {
if (dateR!=null){
setGraphic(btn1);
ImageView icone1 = new ImageView("consult.png");
icone1.setFitHeight(20);
icone1.setFitWidth(20);
btn1.setGraphic(icone1);
setStyle("-fx-alignment : CENTER;");
}
else if (dateR==null){
setGraphic(btn1);
ImageView icone2 = new ImageView("ajout.png");
icone2.setFitHeight(20);
icone2.setFitWidth(20);
btn1.setGraphic(icone2);
setStyle("-fx-alignment : CENTER;");
}
}
}
};
I've got a little problem (it's really not that big of a deal but it annoys me), because after I delete a record from my table, I also want my delete button to disapear, right now it works like this:
BEFORE clicking the DELETE button:
AFTER clicking first record's DELETE button:
Also, this is the function that I'm using to handle this event:
private class ButtonCell extends TableCell<Record, Boolean> {
final Button cellButton = new Button("Delete");
ButtonCell(){
cellButton.setOnAction(new EventHandler<ActionEvent>(){
#Override
public void handle(ActionEvent t){
Alert alert = new Alert(Alert.AlertType.CONFIRMATION);
alert.setTitle("Confirmation Dialog");
alert.setHeaderText("Delete the entire row?");
Optional<ButtonType> result = alert.showAndWait();
if (result.get() == ButtonType.OK){
Animal currentAnimal = (Animal) ButtonCell.this.getTableView().getItems().get(ButtonCell.this.getIndex());
data.remove(currentAnimal);
cellButton.setVisible(false);
}
}
});
}
//Display button if the row is not empty
#Override
protected void updateItem(Boolean t, boolean empty) {
super.updateItem(t, empty);
if(!empty){
setGraphic(cellButton);
}
}
}
Try this:
#Override
protected void updateItem(Boolean t, boolean empty) {
super.updateItem(t, empty);
if(empty){
setGraphic(null);
} else {
setGraphic(cellButton);
}
}
I'm learning JavaFX and i wanted to create a cell factory which is working properly until i want to delete a row from my ListView:
plateList.setCellFactory(new Callback<ListView<Car>, ListCell<Car>>() {
#Override
public ListCell<Car> call(ListView<Car> param) {
ListCell<Car> cell = new ListCell<Car>() {
#Override
protected void updateItem(Car item, boolean empty) {
super.updateItem(item, empty);
if (item != null) {
setText(item.getPlate());
}
}
};
return cell;
}
});
I'm populating the ListView with some sample data:
ObservableList<Car> sample = FXCollections.observableArrayList();
sample.add(new Car("123-abc", "opel", "corsa", 5.5));
sample.add(new Car("123-cba", "vw", "passat", 7.5));
plateList.setItems(sample);
Now i will see what i expect the ListView will be the following:
123-abc
123-cba
How ever if delete a row ex: the first row (123-abc), the ListView will look like this:
123-cba
123-cba
This is the delete part:
#FXML
private void deleteBtnAction() {
plateList.getItems().remove(plateList.getSelectionModel().getSelectedItem());
ObservableList<Car> t = plateList.getItems();
plateList.setItems(t);
}
If i remove the cell factory the program works as intended.
Any help is greatly appreciated.
Try changing to the following, This is required as JavaFX reuses the list cells, so the updateItem() needs to blank unused ones too when passed null
super.updateItem(item, empty);
if (item != null) {
setText(item.getPlate());
} else {
setText(""); // <== clear the now empty cell.
}
Full SSCCE
import javafx.application.Application;
import javafx.collections.FXCollections;
import javafx.collections.ObservableList;
import javafx.scene.Scene;
import javafx.scene.control.Button;
import javafx.scene.control.ListCell;
import javafx.scene.control.ListView;
import javafx.scene.layout.VBox;
import javafx.stage.Stage;
import javafx.util.Callback;
public class JavaFxListView extends Application {
private static class Car {
private String plate;
public Car(String plate, String string2, String string3, double d) {
this.plate = plate;
}
public String getPlate() {
return plate;
}
}
public static void main(String[] args) {
launch(args);
}
#Override
public void start(Stage arg0) throws Exception {
ListView<Car> plateList = new ListView<Car>();
plateList.setCellFactory(new Callback<ListView<Car>, ListCell<Car>>() {
#Override
public ListCell<Car> call(ListView<Car> param) {
ListCell<Car> cell = new ListCell<Car>() {
#Override
protected void updateItem(Car item, boolean empty) {
super.updateItem(item, empty);
if (item != null) {
setText(item.getPlate());
} else {
setText("");
}
}
};
return cell;
}
});
Button delete = new Button("Delete");
ObservableList<Car> sample = FXCollections.observableArrayList();
sample.add(new Car("123-abc", "opel", "corsa", 5.5));
sample.add(new Car("123-cba", "vw", "passat", 7.5));
delete.setOnAction((e) -> {
plateList.getItems().remove(plateList.getSelectionModel().getSelectedItem());
ObservableList<Car> t = plateList.getItems();
plateList.setItems(t);
});
plateList.setItems(sample);
arg0.setScene(new Scene(new VBox(plateList, delete)));
arg0.show();
}
}
According to the Java doc of Cell updateItem method, there is slightly different recomended usage than the accepted one:
protected void updateItem(T item, boolean empty) {
super.updateItem(item, empty);
if (empty || item == null) {
setText(null);
setGraphic(null);
} else {
setText(item.toString());
}
}
The difference is in the usage of parameter empty. But the sollution from #Adam should work correctly in major cases too.
I have a Combo Box in my app . I added some items as string to it . Items have left alignment . I want the right alignment . I searched a lot but i cant find anything.
Thanks.
I figured out the following solution, although I'm not sure if it's a good one:
For button cell:
box.setButtonCell(new ListCell<String>() {
#Override
public void updateItem(String item, boolean empty) {
super.updateItem(item, empty);
if (item != null) {
setText(item);
setAlignment(Pos.CENTER_RIGHT);
Insets old = getPadding();
setPadding(new Insets(old.getTop(), 0, old.getBottom(), 0));
}
}
});
For popup list view:
box.setCellFactory(new Callback<ListView<String>, ListCell<String>>() {
#Override
public ListCell<String> call(ListView<String> list) {
return new ListCell<String>() {
#Override
public void updateItem(String item, boolean empty) {
super.updateItem(item, empty);
if (item != null) {
setText(item);
setAlignment(Pos.CENTER_RIGHT);
setPadding(new Insets(3, 3, 3, 0));
}
}
};
}
});