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");
}
Related
I have a table with many TableItems (Not a tableViewer), when I click on one of the table Items it get selected . The only way to deselect it is by selecting another TableItem. I want to implement a way to deselect The Table selection when The user click on the table Where there is no TableItems, or when ReSelecting the same TableItem.
table.addSelectionListener(new SelectionAdapter() {
#Override
public void widgetSelected(SelectionEvent e) {
if(e.item != ItemSelectioner ) {
ItemSelectioner = (TableItem)e.item;
// Blabla
}else {
ItemSelectioner = null;
table.deselectAll();
//blabla
}
}
});
As you can see, am using a selectionEvent which I think is the probleme, and using:
e.doit = false;
didn't work also.
Selection events are not generated for the empty parts of the table so you can't use a selection listener to do this.
You can use a mouse down listener and check if there is a table item at the mouse location:
table.addListener(SWT.MouseDown, event -> {
TableItem item = table.getItem(new Point(event.x, event.y));
if (item == null) { // No table item at the click location?
table.deselectAll();
}
});
To clear the selection the second time an item is clicked use something like this:
table.addListener(SWT.Selection, new Listener()
{
private int lastSelected = -1;
#Override
public void handleEvent(final Event event)
{
final int selectedIndex = table.getSelectionIndex();
if (selectedIndex < 0) {
lastSelected = -1;
return;
}
if (selectedIndex == lastSelected) {
table.deselect(selectedIndex);
lastSelected = -1;
}
else {
lastSelected = selectedIndex;
}
}
});
I am sucessfull in making a table column editable and updating value to it when the cell is double clicked. Now what I want to do is get the value from a txt field and set the value to a particular cell (column) of the selected row. I have gone through many research but could not find a proper answer. Javafx doesn't allow to directly edit values to a table except directly editing the cell and setting its value.
Thank you
This is a sample of so far what I have done.
Setting cellValueFactory to teh table columns
tblColQuantity.setCellValueFactory(cellData -> cellData.getValue()
.quantityProperty());
tblColQuantity.setCellFactory(col -> new IntegerEditingCell());
tblColRateWithoutvat.setCellValueFactory(cellData -> cellData
.getValue().rateWithoutvatProperty());
tblColRateWithoutvat.setCellFactory(col -> new IntegerEditingCell());
tblColTotalWithvat.setCellValueFactory(cellData -> cellData.getValue().totalWithvatProperty());
tblColTotalWithvat.setCellFactory(col -> new IntegerEditingCell());
The Inner class which helps me update the cell data
public class IntegerEditingCell extends TableCell<AddBillTable, Number> {
private final TextField textField = new TextField();
private final Pattern intPattern = Pattern.compile("\\d*\\.\\d+");
// -?\\d+
public IntegerEditingCell() {
textField.focusedProperty().addListener(
(obs, wasFocused, isNowFocused) -> {
if (!isNowFocused) {
processEdit();
}
});
textField.setOnAction(event -> processEdit());
}
private void processEdit() {
String text = textField.getText();
if (intPattern.matcher(text).matches()) {
commitEdit(Float.parseFloat(text));
} else {
cancelEdit();
}
}
#Override
public void updateItem(Number value, boolean empty) {
super.updateItem(value, empty);
if (empty || value.equals(null)) {
setText(null);
setGraphic(null);
} else if (isEditing()) {
setText(null);
textField.setText(value.toString());
setGraphic(textField);
}/*
* else if (!empty){ textField.setText(value.toString()); }
*/else {
// if((!value.toString().equals(null)) || (value==null)){
setText(value.toString());
setGraphic(null);
System.out.println("Updated");
System.out.println(this.textField.getText());
// }
}
}
#Override
public void startEdit() {
super.startEdit();
Number value = getItem();
if (value != null) {
textField.setText(value.toString());
setGraphic(textField);
setText(null);
}
}
#Override
public void cancelEdit() {
super.cancelEdit();
setText(getItem().toString());
setGraphic(null);
}
// This seems necessary to persist the edit on loss of focus; not sure
// why:
#Override
public void commitEdit(Number value) {
super.commitEdit(value);
// ((PurchaseDetail)this.getTableRow().getItem()).setQuantity(value.floatValue());
System.out.println("Commit edit " + value);
detectEditedCell(value);
}
}
You can just get the selected item from the table, and call the appropriate set method corresponding to the property that the column represents.
For example, if you wanted a text field to update the quantity of the currently selected row, you would do:
TextField textField = new TextField();
textField.setOnAction(e -> {
AddBillTable selectedItem = table.getSelectionModel().getSelectedItem();
if (selectedItem != null) {
selectedItem.setQuantity(Integer.parseInt(textField.getText()));
}
});
As long as you are implementing your model class (AddBillTable in your example) with JavaFX observable properties (StringProperty, IntegerProperty, etc), then changing the property value will automatically update the table.
I have a situation where I have a TableView. I'm trying to implement a feature that allows a cell to be moved up or down. After moving the cell up or down (with the cell content), I want to change focus to the new location of the cell.
The problem is that it doesn't change to the new location. It for some reason stays in the original selected cell location.
This is the code used to move up, move down and change focus:
I am attempting to move a single selected cell.
public class TableController
{
private ObservableList<SimpleStringProperty> observablePrnPropertyData;
#FXML
private TableView<SimpleStringProperty> table;
#FXML
private TableColumn<SimpleStringProperty, String> data;
#FXML
private void initialize()
{
this.data.setCellValueFactory(cellData -> cellData.getValue());
this.data.setCellFactory(event -> new EditCell(this.observablePrnPropertyData, this.table));
}
public void display(final PrnProperty prnProperty)
{
this.observablePrnPropertyData = PrnPropertyUtil.getObservableDataFromPrnProperty(prnProperty);
this.table.setItems(this.observablePrnPropertyData);
}
private final class EditCell extends TableCell<SimpleStringProperty, String>
{
#Override
public void updateItem(String item, boolean empty)
{
super.updateItem(item, empty);
if (empty)
{
this.setText(null);
this.setGraphic(null);
}
else
{
this.setUpContextMenu();
this.setText(this.getString());
this.setGraphic(null);
}
}
private void setUpContextMenu()
{
// Context menu
ContextMenu contextMenu = new ContextMenu();
// context menu Move up
this.moveUp = new MenuItem("Move up");
this.moveUp.setOnAction(event -> this.moveUp(this.table, this.observablePrnPropertyData));
contextMenu.getItems().add(this.moveUp);
// Context menu for move down
this.moveDown = new MenuItem("Move down");
this.moveDown.setOnAction(event -> this.moveDown(this.table, this.observablePrnPropertyData));
contextMenu.getItems().add(this.moveDown);
// Add context menu
this.setContextMenu(contextMenu);
}
public void moveUp(final TableView<?> table, ObservableList listToManipulate)
{
final int selectedIndex = table.getSelectionModel().getSelectedIndex();
final Object removeItem = listToManipulate.remove(selectedIndex);
final int newIndex = selectedIndex - 1;
listToManipulate.add(newIndex, removeItem);
this.changeTableCellFocus(table, newIndex);
}
public void moveDown(final TableView<?> table, ObservableList listToManipulate)
{
final int selectedIndex = table.getSelectionModel().getSelectedIndex();
final Object remove = listToManipulate.remove(selectedIndex);
final int newIndex = selectedIndex + 1;
listToManipulate.add(newIndex, remove);
this.changeTableCellFocus(table, newIndex);
}
public void changeTableCellFocus(final TableView<?> table, final int focusIndex)
{
table.requestFocus();
table.getSelectionModel().clearAndSelect(focusIndex);
table.getFocusModel().focus(focusIndex);
}
}
}
It would be great if someone can give a working example. I really want to know what i'm doing wrong.
If you just have to focus next and previous line, you can try: table.getFocusModel().focusNext() and .focusPrevious(), like described here: http://docs.oracle.com/javafx/2/api/javafx/scene/control/FocusModel.html
For your case use this code will work fine, it is simple you must first get the selected index, move the item to the next (or previous) row, then select this new row.
void upClicked(){
if(table.getSelectionModel().getSelectedItem() != null) // check if the user really selected a row in the table
{
if(table.getSelectionModel().getSelectedIndex() != 0) // if the row first one so do nothing
{
int index = table.getSelectionModel().getSelectedIndex(); // get the selected row index
SimpleStringProperty x = table.getSelectionModel().getSelectedItem(); // get the selected item
table.getItems().set(index, table.getItems().get(index-1)); // move the selected item up
table.getItems().set(index-1, x); // change the row with the item in above
table.getSelectionModel().select(index-1); // select the new row position
}
}
}
void downClicked(){
if(table.getSelectionModel().getSelectedItem() != null)
{
if(table.getSelectionModel().getSelectedIndex() != table.getItems().size()-1) // if the row is in last so dont do nothing
{
int index = table.getSelectionModel().getSelectedIndex();
SimpleStringProperty x = table.getSelectionModel().getSelectedItem();
table.getItems().set(index, table.getItems().get(index+1));
table.getItems().set(index+1, x);
table.getSelectionModel().select(index+1);
}
}
}
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;
}
});
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