Detect doubleclick on cell of TableView JavaFX - java

I am trying to detect a doubleclick on a random cell of a tableview.
The detection of the doubleclick is not a problem but rather which cell has been doubleclicked.
table.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());
}
}
});
This is how I'm building my table:
private void BuildTable() throws Exception
{
/*Some initialisations etc*/
for(int i=0; i<result.getMetaData().getColumnCount();i++)
{
final int j = i;
TableColumn col = new TableColumn(result.getMetaData().getColumnName(i+1));
col.setCellValueFactory(new Callback<CellDataFeatures<ObservableList,String>,ObservableValue<String>>(){
public ObservableValue<String> call(CellDataFeatures<ObservableList, String> param)
{
return new SimpleStringProperty(param.getValue().get(j).toString());
}
});
table.getColumns().addAll(col);
}
while(result.next()){
ObservableList<String> row = FXCollections.observableArrayList();
for(int i = 1; i<=result.getMetaData().getColumnCount();i++){
row.add(result.getString(i));
}
data.add(row);
}
table.setItems(data);
}catch(Exception e){
e.printStackTrace();
}
}
The real problem here is that I can't just typecast into a TableCell.
Can someone help me out? I would be very grateful.

Instead of registering a handler with the table, you need to register the handler with the table cells themselves. To do this, use a cell factory on the appropriate TableColumn(s).
As an example, add the following code to the standard table example (listing 13.6).
firstNameCol.setCellFactory(new Callback<TableColumn<Person, String>, TableCell<Person, String>>() {
#Override
public TableCell<Person, String> call(TableColumn<Person, String> col) {
final TableCell<Person, String> cell = new TableCell<Person, String>() {
#Override
public void updateItem(String firstName, boolean empty) {
super.updateItem(firstName, empty);
if (empty) {
setText(null);
} else {
setText(firstName);
}
}
};
cell.addEventHandler(MouseEvent.MOUSE_CLICKED, new EventHandler<MouseEvent>() {
#Override
public void handle(MouseEvent event) {
if (event.getClickCount() > 1) {
System.out.println("double click on "+cell.getItem());
}
}
});
return cell ;
}
});

You need set the tableview's selection mode in cell, defined a custom type cell where you catch the keypress event or double clic, for an example you can review this page
https://gist.github.com/james-d/be5bbd6255a4640a5357

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.

How to detect double click on ComboBox (TableCell Combobox) in JavaFx?

I am Creating a tableview with 2 columns and each populated cell of this table is a ComboBox. This is how I am implementing this
private void loadTable() {
vBox.getChildren().clear();
this.tableView = new TableView<>();
tableView.setEditable(true);
TableColumn<CustomClass, Products> prodCol = createComboBoxColumn("products", "Product", Products.class,
productObList);
prodCol.setMinWidth(300);
prodCol.setOnEditCommit(e -> {
if (!e.getTableView().getItems().isEmpty()) {
System.out.println("e.getNewValue().getId(): " + e.getNewValue().getId());
Products product = productsModel.getProductsById(e.getNewValue().getId());
e.getTableView().getItems().get(e.getTablePosition().getRow()).setProducts(product);
}
tableView.refresh();
});
tableView.getColumns().add(prodCol);
TableColumn<CustomClass, Brand> brandCol = createComboBoxColumn("brand", "Brand", Brand.class,
brandObList);
brandCol.setMinWidth(300);
brandCol.setOnEditCommit(e -> {
if (!e.getTableView().getItems().isEmpty()) {
System.out.println("e.getNewValue().getId(): " + e.getNewValue().getId());
Brand brand = brandModel.getBrandById(e.getNewValue().getId());
e.getTableView().getItems().get(e.getTablePosition().getRow()).setBrand(brand);
}
tableView.refresh();
});
tableView.getColumns().add(brandCol);
// tableView.addEventHandler(MouseEvent.MOUSE_CLICKED, new EventHandler<MouseEvent>() {
// #Override
// public void handle(MouseEvent event) {
// if (event.getClickCount() == 2) {
// System.out.println("on Click");
// if (event.getTarget() instanceof ComboBox) {
// System.out.println(((ComboBox) event.getTarget()).getSelectionModel().getSelectedItem());
// }
// }
// }
// });
tableView.getItems().addAll(brandManifestCustomObList);
vBox.getChildren().addAll(tableView);
}
and the createComboboxColumn Method as
public static <T> TableColumn<CustomClass, T> createComboBoxColumn(String name, String columHeading,
Class<T> type, ObservableList list) {
TableColumn<CustomClass, T> column = new TableColumn<>(columHeading);
column.setCellFactory(ComboBoxTableCell.forTableColumn(list));
column.setResizable(true);
column.setMaxWidth(100);
column.setEditable(true);
column.setCellValueFactory(new PropertyValueFactory<>(name));
return column;
}
What I am not able to achieve is to detect double click(or even single click) on the tablecell combo box. Once I select some other row and come back to this tablecell and doubleclick, only then I am able to detect the double click on the cell.
The obvious reason is once I click on a cell it becomes a Combobox and I have not written code to detect it, because I don't understand how to in this case.
Also, if possible, I need to detect the column index of the cell in the tableview.
Following questions do not answer what I am looking for:
1) JavaFx: ComboBox Table Cell double click
2) Detect mouse click on SELECTION Editable ComboBox JavaFX
3) Select JavaFX Editable Combobox text on click
Probably you can use
tableView.addEventFilter(MouseEvent.MOUSE_CLICKED, new EventHandler<MouseEvent>() {
#Override
public void handle(MouseEvent event) {
if (event.getClickCount() == 2) {
System.out.println("on Click");
if (event.getTarget() instanceof ComboBox) {
System.out.println(((ComboBox) event.getTarget()).getSelectionModel().getSelectedItem());
}
if (event.getTarget() instanceof ComboBoxTableCell<?,?>) {
System.out.println(((ComboBoxTableCell) event.getTarget()).getItem().toString());
}
}
}
});
because the table cell has now changed to a ComboBoxTableCell.
To get the column of the tableView I got some idea from here given by James_D.
You can use
TablePosition pos = tableView.getSelectionModel().getSelectedCells().get(0);
int row = pos.getRow();
BrandManifestCustom bmc = tableView.getItems().get(row);
TableColumn col = pos.getTableColumn();
if (col.getCellObservableValue(bmc).getValue() instanceof Products) {
System.out.println("hey Products");
}
if (col.getCellObservableValue(bmc).getValue() instanceof Brand) {
System.out.println("hey Brand");
}

Javafx Bug? Tableview is not rendering correctly some rows

Actually I have a problem in my JavaFX app using TableView. I don't no why but, when I load data to TableView during runtime, the JavaFX is not rendering some rows, as you can see in the picture bellow:
But, when I resize the column, the data is displayed:
Bellow follow the source code used:
public void loadData()
{
// Define the TableView columns using Reflection defined by ResultSetMetadata
ArrayList<TableColumn> gridColumns = defineGridColumns(data.get(0));
this.tableView.getColumns().clear();
this.tableView.getColumns().addAll(gridColumns);
// Load data to TableView
this.tableView.setItems(FXCollections.observableArrayList(data));
}
private void defineGridColumns(Object singleData)
{
ArrayList<TableColumn> gridColumns = new ArrayList<>();
Field[] fields = singleData.getClass().getFields();
for (int i = 0; i < fields.length; i++)
{
TableColumn column = createTableColumn(fields[i].getName());
this.gridColumns.add(column);
}
return gridColumns;
}
private TableColumn createTableColumn(String columnName)
{
TableColumn column = new TableColumn(columnName);
column.setCellValueFactory(new PropertyValueFactory(columnName));
column.setPrefWidth(columnName.length() * 20);
HBox box = new HBox();
box.setAlignment(Pos.CENTER);
box.prefWidthProperty().bind(column.widthProperty().subtract(5));
box.setSpacing(10.0);
box.getChildren().addAll(new Label(column.getText()));
column.setGraphic(box);
// Align the cell content in center
column.setCellFactory(new Callback<TableColumn, TableCell>()
{
#Override
public TableCell call(TableColumn param)
{
TableCell cell = new TableCell()
{
#Override
public void updateItem(Object item, boolean empty)
{
if (item != null)
{
setText(item.toString());
}
}
};
cell.setAlignment(Pos.CENTER);
return cell;
}
});
return column;
}
Well, what I'm doing wrong? I already update my Java to the lastest version (JDK 1.8.11).
Thanks to everybody!
Palliative Solution
As resizing column width triggers JavaFX to display the data, I did a method that changes all columns sizes after the data is loaded:
public void loadData()
{
// Define the TableView columns using Reflection defined by ResultSetMetadata
ArrayList<TableColumn> gridColumns = defineGridColumns(data.get(0));
this.tableView.getColumns().clear();
this.tableView.getColumns().addAll(gridColumns);
// Load data to TableView
this.tableView.setItems(FXCollections.observableArrayList(data));
// HACK to force JavaFX display all data
final TrendAnalysisTabController thisController = this;
Platform.runLater(new Runnable()
{
#Override
public void run()
{
thisController.adjustAllColumnWidths();
}
});
}
private void adjustAllColumnWidths()
{
TableViewSkin<?> skin = (TableViewSkin<?>) this.tableView.getSkin();
TableHeaderRow headerRow = skin.getTableHeaderRow();
NestedTableColumnHeader rootHeader = headerRow.getRootHeader();
for (TableColumnHeader columnHeader : rootHeader.getColumnHeaders())
{
try
{
TableColumn<?, ?> column = (TableColumn<?, ?>) columnHeader.getTableColumn();
if (column != null)
{
// Changes the width column and rollback it
double prefWidth = column.getPrefWidth();
column.setPrefWidth(prefWidth + 0.01);
column.setPrefWidth(prefWidth);
}
}
catch (Throwable e)
{
log.log(Level.SEVERE, "Error adjusting columns widths: " + e.getMessage(), e);
}
}
}
because TableCells are reused, you need to explicitly "clear" them in your updateItem method:
#Override
public void updateItem(Object item, boolean empty) {
super.updateItem(item, empty);
if (item == null || empty) {
setText(null);
} else{
setText(item.toString());
}
}
I'm not sure if this fixes your problem, but when creating a TableCell implementation, your updateItem(...) method must call super.updateItem(...):
column.setCellFactory(new Callback<TableColumn, TableCell>()
{
#Override
public TableCell call(TableColumn param)
{
TableCell cell = new TableCell()
{
#Override
public void updateItem(Object item, boolean empty)
{
// Don't omit this:
super.updateItem(item, empty);
if (item != null)
{
setText(item.toString());
}
}
};
cell.setAlignment(Pos.CENTER);
return cell;
}
});

Adding different buttons to TableView

I have discovered how to insert a button inside a row's TableView but I'm not sure how could I add different values for that button:
column
.setCellFactory(new Callback<TableColumn<GuiObject, Boolean>, TableCell<GuiObject, Boolean>>() {
#Override
public TableCell<GuiObject, Boolean> call(
TableColumn<GuiObject, Boolean> p) {
return new ButtonCell();
}
});
Where ButtonCell is self implemented button for TableCell<GuiObject, Boolean>, but I want to be able to dynamically insert different buttons dependent on the row I am inserting.
If you access the TableView from java like this:
#FXML
TableView myTable;
#Override
public void initialize(URL url, ResourceBundle rb) {
TableColumn<Item, String> firstColumn = new TableColumn<>("First Column");
firstColumn.setCellValueFactory(new Callback<TableColumn.CellDataFeatures<Item, String>, ObservableValue<String>>() {
#Override
public ObservableValue<String> call(TableColumn.CellDataFeatures<Item, String> p) {
return new ReadOnlyObjectWrapper(p.getValue().column1);
}
});
TableColumn<Item, String> secondColumn = new TableColumn<>("Second Column");
secondColumn.setCellValueFactory(new Callback<TableColumn.CellDataFeatures<Item, String>, ObservableValue<String>>() {
#Override
public ObservableValue<String> call(TableColumn.CellDataFeatures<Item, String> p) {
return new ReadOnlyObjectWrapper(p.getValue().column2);
}
});
TableColumn<Item, Button> buttonCol = new TableColumn<>("ButtonColumn");
buttonCol.setSortable(false);
buttonCol.setCellValueFactory(new Callback<TableColumn.CellDataFeatures<Item, Button>, ObservableValue<Button>>() {
#Override
public ObservableValue<Button> call(TableColumn.CellDataFeatures<Item, Button> features) {
return new ReadOnlyObjectWrapper(features.getValue().button);
}
});
myTable.getColumns().add(buttonCol);
myTable.getColumns().add(firstColumn);
myTable.getColumns().add(secondColumn);
myTable.getItems().add(new Item("Test 1", "Test 1", new Button("Test 1"), new EventHandler<ActionEvent>() {
#Override
public void handle(ActionEvent t) {
//ON ACTION CODE HERE
System.out.println("TEST 1 CLICKED!");
}
}));
myTable.getItems().add(new Item("Test 2", "Test 2", new Button("Test 2"), new EventHandler<ActionEvent>() {
#Override
public void handle(ActionEvent t) {
//ON ACTION CODE HERE
System.out.println("TEST 2 BUTTON CLICKED");
}
}));
}
And the Item class:
public class Item {
public String column1, column2;
public Button button;
public Item(String column1, String column2, Button b) {
this.column1 = column1;
this.column2 = column2;
button = b;
}
}
Proof it works:
I fixed my final issue thanks to the code from Cobbles. I simply added a method addAddButton() which adds a button for new row and provides the functionality for the existing objects of type Item:
private void addAddButton() {
Button b = new Button(" + ");
final Item item = new Item(="<new>",
"<new>", b);
b.setOnAction(new EventHandler<ActionEvent>() {
#Override
public void handle(ActionEvent t) {
Button deleteButton = new Button(" - ");
deleteButton.setOnAction(new EventHandler<ActionEvent>() {
#Override
public void handle(ActionEvent t) {
selectedItems.remove(item); // a predefined list of selected items
myTable.getItems().clear();
for (Item current : selectedItems) {
myTable.getItems().add(current);
}
addAddButton();
}
});
item.setButton(deleteButton);
selectedItems.add(item);
myTable.getItems().clear();
for (Item current : selectedItems) {
myTable.getItems().add(current);
}
addAddButton();
}
});
tableAvailabilites.getItems().add(ga1);
}
It is not super efficient yet, but it is working!

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.

Categories