I am making an application using Scenebuilder with JavaFX.
I have three inputs for a TableView:
Two TextField input1, input2.
One DatePicker.
When one or more of the input fields is empty and i click on the addButton, the object is added to the TableView.
How do I show an error popup which appears whenever i click on addButton and at least one field (input1, input2) is empty ?
addButton.setOnAction(new EventHandler<ActionEvent>() {
#Override public void handle(ActionEvent e) {
if ((input1.getText() != null && !input1.getText().isEmpty()) &&
(input2.getText() != null && !input2.getText().isEmpty())){
//ADD CODE TO ADD THE ITEM HERE!
} else {
Alert alert = new Alert(AlertType.INFORMATION);
alert.setTitle("Input fields empty");
alert.setContentText("Please fill all input fields");
alert.showAndWait();
}
}
});
PS : Here you can find different Alert Types depending on your needs.
I have an editable tableview with editable column with this cell factory
tblColumn.setCellFactory(t -> {
ComboBoxTableCell myComboBoxTableCell = new ComboBoxTableCell();
myComboBoxTableCell.setOnKeyPressed(e -> {
System.out.println("key pressed");
KeyCode code = e.getCode();
System.out.println("code "+code);
});
myComboBoxTableCell.setComboBoxEditable(true);
return myComboBoxTableCell;
});
what i'm trying to do is to get letter typed in the editable comboBoxTableCell, but when i type, the onKeyPressed method is not fired. am i doing something wrong?
PLease help.
I have a TableView, where I need to enable the selection of any cells(one at a time). For now, I use this code:
tableView.getSelectionModel().selectedItemProperty().addListener(new ChangeListener() {
#Override
public void changed(ObservableValue observableValue, Object oldValue, Object newValue) {
if(tableView.getSelectionModel().getSelectedItem() != null)
{
TableView.TableViewSelectionModel selectionModel = tableView.getSelectionModel();
ObservableList selectedCells = selectionModel.getSelectedCells();
TablePosition tablePosition = (TablePosition) selectedCells.get(0);
String val = (String)tablePosition.getTableColumn().getCellData(newValue);
System.out.println("Selected Value: " + val);
selectionTextField.appendText(val);
}
}
});
The problem is, I can't get the values of different cells in the same row after each other. I think it's because it's the same row, the selection listener is not triggered. I tried clearSelection(), but then I get out of bounds exception, and I read somewhere that I cant change the table model in the same listener. Any tips?
Thank you.
If you are using cell selection instead of row selection (in other words, if you have called tableView.getSelectionMode().setCellSelectionEnabled(true);), then you should observe the list of selected cells instead of the selectedItem property. The selectedItem property only indicates the row that is selected, so it only changes if you select a new row.
ObservableList<TablePosition> selectedCells = table.getSelectionModel().getSelectedCells() ;
selectedCells.addListener((ListChangeListener.Change<? extends TablePosition> change) -> {
if (selectedCells.size() > 0) {
TablePosition selectedCell = selectedCells.get(0);
TableColumn column = selectedCell.getTableColumn();
int rowIndex = selectedCell.getRow();
Object data = column.getCellObservableValue(rowIndex).getValue();
}
});
What Event is called in JavaFX, when a Node is focused?
I have TextField, which can be focused either by mouse (setOnMouseClicked) or by TAB key from other Node (here my problem comes in).
How can I handle the second focus possibility? Is there a way how to handle both at once?
You can listen to the focused property:
TextField tf = new TextField();
TextField tf2 = new TextField();
tf.focusedProperty().addListener((observable, oldValue, newValue) -> {
if (newValue) {
System.out.println("Node 1: Mine!");
}
});
tf2.focusedProperty().addListener((observable, oldValue, newValue) -> {
if (newValue) {
System.out.println("Node 2: Mine!");
}
});
Scene scene = new Scene(new VBox(tf, tf2), 300, 250);
If you change the focus, you can observe see the 2 TextField "arguing who's got the focus".
I've created a TableView where each cell contains a TextField or a CheckBox. In the TableView you're supposed to be able to navigate left and right between cells using TAB and SHIFT+TAB, and up and down between cells using the UP and DOWN keys.
This works perfectly when a text field cell is focused. But when a check box cell is focused, the tab funcationality behaves strange. You can tab in the opposite direction of the cell you tabbed from, but you can't switch tab direction.
So for instance if you tabbed to the check box cell using only the TAB key, then SHIFT+TAB wont work. But if you tab to the next cell using the TAB key, and then TAB back using SHIFT+TAB (assuming the next cell is a text field cell), then TAB wont work.
I've tried running any code handling focus on the UI thread using Platform.runLater(), without any noteable difference. All I know is that the TAB KeyEvent is properly catched, but the check box cell and the check box never loses focus in the first place anyway. I've tried for instance removing its focus manually by doing e.g. getParent().requestFocus() but that just results in the parent being focused instead of the next cell. What makes it strange is that the same code is executed and working properly when you tab in the opposite direction of the cell you came from.
Here's a MCVE on the issue. Sadly it does not really live up to the "M" of the abbreviation:
import java.util.List;
import javafx.application.Application;
import javafx.beans.property.SimpleStringProperty;
import javafx.beans.property.StringProperty;
import javafx.collections.FXCollections;
import javafx.collections.ObservableList;
import javafx.scene.Scene;
import javafx.scene.control.CheckBox;
import javafx.scene.control.TableCell;
import javafx.scene.control.TableColumn;
import javafx.scene.control.TableView;
import javafx.scene.control.TextField;
import javafx.scene.input.KeyCode;
import javafx.scene.input.KeyEvent;
import javafx.stage.Stage;
public class AlwaysEditableTable extends Application {
public void start(Stage stage) {
TableView<ObservableList<StringProperty>> table = new TableView<>();
table.setEditable(true);
table.getSelectionModel().setCellSelectionEnabled(true);
table.setPrefWidth(510);
// Dummy columns
ObservableList<String> columns = FXCollections.observableArrayList("Column1", "Column2", "Column3", "Column4",
"Column5");
// Dummy data
ObservableList<StringProperty> row1 = FXCollections.observableArrayList(new SimpleStringProperty("Cell1"),
new SimpleStringProperty("Cell2"), new SimpleStringProperty("0"), new SimpleStringProperty("Cell4"),
new SimpleStringProperty("0"));
ObservableList<StringProperty> row2 = FXCollections.observableArrayList(new SimpleStringProperty("Cell1"),
new SimpleStringProperty("Cell2"), new SimpleStringProperty("1"), new SimpleStringProperty("Cell4"),
new SimpleStringProperty("0"));
ObservableList<StringProperty> row3 = FXCollections.observableArrayList(new SimpleStringProperty("Cell1"),
new SimpleStringProperty("Cell2"), new SimpleStringProperty("1"), new SimpleStringProperty("Cell4"),
new SimpleStringProperty("0"));
ObservableList<ObservableList<StringProperty>> data = FXCollections.observableArrayList(row1, row2, row3);
for (int i = 0; i < columns.size(); i++) {
final int j = i;
TableColumn<ObservableList<StringProperty>, String> col = new TableColumn<>(columns.get(i));
col.setCellValueFactory(param -> param.getValue().get(j));
col.setPrefWidth(100);
if (i == 2 || i == 4) {
col.setCellFactory(e -> new CheckBoxCell(j));
} else {
col.setCellFactory(e -> new AlwaysEditingCell(j));
}
table.getColumns().add(col);
}
table.setItems(data);
Scene scene = new Scene(table);
stage.setScene(scene);
stage.show();
}
public static void main(String[] args) {
launch();
}
/**
* A cell that contains a text field that is always shown.
*/
public static class AlwaysEditingCell extends TableCell<ObservableList<StringProperty>, String> {
private final TextField textField;
public AlwaysEditingCell(int columnIndex) {
textField = new TextField();
this.emptyProperty().addListener((obs, wasEmpty, isNowEmpty) -> {
if (isNowEmpty) {
setGraphic(null);
} else {
setGraphic(textField);
}
});
// The index is not changed until tableData is instantiated, so this
// ensure the we wont get a NullPointerException when we do the
// binding.
this.indexProperty().addListener((obs, oldValue, newValue) -> {
ObservableList<ObservableList<StringProperty>> tableData = getTableView().getItems();
int oldIndex = oldValue.intValue();
if (oldIndex >= 0 && oldIndex < tableData.size()) {
textField.textProperty().unbindBidirectional(tableData.get(oldIndex).get(columnIndex));
}
int newIndex = newValue.intValue();
if (newIndex >= 0 && newIndex < tableData.size()) {
textField.textProperty().bindBidirectional(tableData.get(newIndex).get(columnIndex));
setGraphic(textField);
} else {
setGraphic(null);
}
});
// Every time the cell is focused, the focused is passed down to the
// text field and all of the text in the textfield is selected.
this.focusedProperty().addListener((obs, oldValue, newValue) -> {
if (newValue) {
textField.requestFocus();
textField.selectAll();
System.out.println("Cell focused!");
}
});
// Switches focus to the cell below if ENTER or the DOWN arrow key
// is pressed, and to the cell above if the UP arrow key is pressed.
// Works like a charm. We don't have to add any functionality to the
// TAB key in these cells because the default tab behavior in
// JavaFX works here.
this.addEventFilter(KeyEvent.KEY_RELEASED, e -> {
if (e.getCode().equals(KeyCode.UP)) {
getTableView().getFocusModel().focus(getIndex() - 1, this.getTableColumn());
e.consume();
} else if (e.getCode().equals(KeyCode.DOWN)) {
getTableView().getFocusModel().focus(getIndex() + 1, this.getTableColumn());
e.consume();
} else if (e.getCode().equals(KeyCode.ENTER)) {
getTableView().getFocusModel().focus(getIndex() + 1, this.getTableColumn());
e.consume();
}
});
}
}
/**
* A cell containing a checkbox. The checkbox represent the underlying value
* in the cell. If the cell value is 0, the checkbox is unchecked. Checking
* or unchecking the checkbox will change the underlying value.
*/
public static class CheckBoxCell extends TableCell<ObservableList<StringProperty>, String> {
private final CheckBox box;
public CheckBoxCell(int columnIndex) {
this.box = new CheckBox();
this.emptyProperty().addListener((obs, wasEmpty, isNowEmpty) -> {
if (isNowEmpty) {
setGraphic(null);
} else {
setGraphic(box);
}
});
this.indexProperty().addListener((obs, oldValue, newValue) -> {
// System.out.println("Row: " + getIndex() + ", Column: " +
// columnIndex + ". Old index: " + oldValue
// + ". New Index: " + newValue);
ObservableList<ObservableList<StringProperty>> tableData = getTableView().getItems();
int newIndex = newValue.intValue();
if (newIndex >= 0 && newIndex < tableData.size()) {
// If active value is "1", the check box will be set to
// selected.
box.setSelected(tableData.get(getIndex()).get(columnIndex).equals("1"));
// We add a listener to the selected property. This will
// allow us to execute code every time the check box is
// selected or deselected.
box.selectedProperty().addListener((observable, oldVal, newVal) -> {
if (newVal) {
// If newValue is true the checkBox is selected, and
// we set the corresponding cell value to "1".
tableData.get(getIndex()).get(columnIndex).set("1");
} else {
// Otherwise we set it to "0".
tableData.get(getIndex()).get(columnIndex).set("0");
}
});
setGraphic(box);
} else {
setGraphic(null);
}
});
// If I listen to KEY_RELEASED instead, pressing tab next to a
// checkbox will make the focus jump past the checkbox cell. This is
// probably because the default TAB functionality is invoked on key
// pressed, which switches the focus to the check box cell, and then
// upon release this EventFilter catches it and switches focus
// again.
this.addEventFilter(KeyEvent.KEY_PRESSED, e -> {
if (e.getCode().equals(KeyCode.UP)) {
System.out.println("UP key pressed in checkbox");
getTableView().getFocusModel().focus(getIndex() - 1, this.getTableColumn());
e.consume();
} else if (e.getCode().equals(KeyCode.DOWN)) {
System.out.println("DOWN key pressed in checkbox");
getTableView().getFocusModel().focus(getIndex() + 1, this.getTableColumn());
e.consume();
} else if (e.getCode().equals(KeyCode.TAB)) {
System.out.println("Checkbox TAB pressed!");
TableColumn<ObservableList<StringProperty>, ?> nextColumn = getNextColumn(!e.isShiftDown());
if (nextColumn != null) {
getTableView().getFocusModel().focus(getIndex(), nextColumn);
}
e.consume();
// ENTER key will set the check box to selected if it is
// unselected and vice versa.
} else if (e.getCode().equals(KeyCode.ENTER)) {
box.setSelected(!box.isSelected());
e.consume();
}
});
// Tracking the focused property of the check box for debug
// purposes.
box.focusedProperty().addListener((obs, oldValue, newValue) ->
{
if (newValue) {
System.out.println("Box focused on index " + getIndex());
} else {
System.out.println("Box unfocused on index " + getIndex());
}
});
// Tracking the focused property of the check box for debug
// purposes.
this.focusedProperty().addListener((obs, oldValue, newValue) ->
{
if (newValue) {
System.out.println("Box cell focused on index " + getIndex());
box.requestFocus();
} else {
System.out.println("Box cell unfocused on index " + getIndex());
}
});
}
/**
* Gets the column to the right or to the left of the current column
* depending no the value of forward.
*
* #param forward
* If true, the column to the right of the current column
* will be returned. If false, the column to the left of the
* current column will be returned.
*/
private TableColumn<ObservableList<StringProperty>, ?> getNextColumn(boolean forward) {
List<TableColumn<ObservableList<StringProperty>, ?>> columns = getTableView().getColumns();
// If there's less than two columns in the table view we return null
// since there can be no column to the right or left of this
// column.
if (columns.size() < 2) {
return null;
}
// We get the index of the current column and then we get the next
// or previous index depending on forward.
int currentIndex = columns.indexOf(getTableColumn());
int nextIndex = currentIndex;
if (forward) {
nextIndex++;
if (nextIndex > columns.size() - 1) {
nextIndex = 0;
}
} else {
nextIndex--;
if (nextIndex < 0) {
nextIndex = columns.size() - 1;
}
}
// We return the column on the next index.
return columns.get(nextIndex);
}
}
}
After some digging in the TableView source code I found the issue. Here's the source code for the focus(int row, TableColumn<S, ?> column) method:
#Override public void focus(int row, TableColumn<S,?> column) {
if (row < 0 || row >= getItemCount()) {
setFocusedCell(EMPTY_CELL);
} else {
TablePosition<S,?> oldFocusCell = getFocusedCell();
TablePosition<S,?> newFocusCell = new TablePosition<>(tableView, row, column);
setFocusedCell(newFocusCell);
if (newFocusCell.equals(oldFocusCell)) {
// manually update the focus properties to ensure consistency
setFocusedIndex(row);
setFocusedItem(getModelItem(row));
}
}
}
The issue arises when newFocusCell is compared to oldFocusCell. When tabbing to a checkbox cell the cell would for some reason not get set as the focused cell. Hence the focusedCell property returned by getFocusedCell() will be the cell we focused before the check box cell. So when we then try to focus that previous cell again, newFocusCell.equals(oldFocusCell) will return true, and the focus will be set to the currently focused cell again by doing:
setFocusedIndex(row);
setFocusedItem(getModelItem(row));`
So what I had to do was make sure that the cell isn't be the value of the focusedCell property when we want to focus it. I solved this by setting the focus manually to the whole table before trying to switch the focus from the check box cell:
table.requestFocus();