Is there a way to make TableView in javafx to act just like JTable in swing?
The current procedure in TableView edit a cell content is:
Select the cell.
Pressing Enter on the Cell to get in Edit mode.
Typing.
Pressing Enter to commit edit.
while in jtable is much easier; you don't have to press enter to edit; you can edit directly.
Any ideas to let javafx table view act like jtable in this point?
I do it with a bunch of listeners. I'll add snips of my code since the columns won't really apply to you.
public class DataTable extends TableView<LineItem> {
private final Data data;
//used for keyreleased in table to put table in editing state and remember which key was pressed
private String lastKey = null;
/** makes a table
* #param data The Data structure
*/
public DataTable(Data data){
super(data.lines);
this.data = data;
setEditable(true);
//cut out my deleted lines stack but left some code because has to handle del in textfield
Callback<TableColumn<LineItem,String>, TableCell<LineItem,String>> txtCellFactory =
(TableColumn<LineItem,String> p) -> {return new EditingCell();};
TableColumn<LineItem,String> lineNoCol = new TableColumn<>("Line");
lineNoCol.setCellValueFactory(new PropertyValueFactory<>("LineNo"));
lineNoCol.setCellFactory(txtCellFactory);
lineNoCol.setOnEditCommit((TableColumn.CellEditEvent<LineItem, String> evt) -> {
evt.getTableView().getItems().get(evt.getTablePosition().getRow())
.setLineNo(evt.getNewValue());
});
//more columns
///// This puts it in editing state and remebers the key pressed for the textfield
setOnKeyPressed((KeyEvent t) -> {
TablePosition tp;
if (!t.isControlDown() &&
(t.getCode().isLetterKey() || t.getCode().isDigitKey())) {
lastKey = t.getText();
tp = getFocusModel().getFocusedCell();
edit(tp.getRow(),tp.getTableColumn());
lastKey = null;
}
});
addEventFilter(KeyEvent.KEY_PRESSED, (KeyEvent t) -> {
if (getEditingCell() == null && t.getCode() == KeyCode.ENTER) {
if (t.isShiftDown()) {
getSelectionModel().selectAboveCell();
} else {
getSelectionModel().selectBelowCell();
}
t.consume();
}
if (t.isControlDown() && t.getCode() == KeyCode.TAB) {
if (t.isShiftDown()) {
getSelectionModel().selectLeftCell();
} else {
getSelectionModel().selectRightCell();
}
t.consume();
}
});
setOnKeyReleased((KeyEvent t) -> {
TablePosition tp;
switch (t.getCode()) {
case INSERT:
data.lines.add(new LineItem());//maybe try adding at position
getSelectionModel().selectLast();
break;
case DELETE:
//textfield has to consume this so it doesn't reach here.
tp = getSelectionModel().getSelectedCells().get(0);
if (tp.getTableColumn() == lineNoCol) {
deletedLines.push(data.lines.remove(tp.getRow()));
//todo reselect something
} else { //maybe delete cell value
}
break;
case Z:
if (t.isControlDown()) {
if (!deletedLines.isEmpty()) {
data.lines.add(deletedLines.pop());
//todo re-sort etc
}
}
}
});
}
private class EditingCell extends TableCell {//no type for now
private TextField textField;
#Override
public void startEdit() {
if (!isEmpty()) {
super.startEdit();
createTextField();
setText(null);
setGraphic(textField);
Platform.runLater(() -> {//without this space erases text, f2 doesn't
textField.requestFocus();//also selects
});
if (lastKey != null) {
textField.setText(lastKey);
Platform.runLater(() -> {
textField.deselect();
textField.end();
});
}
}
}
#Override
public void cancelEdit() {
super.cancelEdit();
try {
setText(getItem().toString());
} catch (Exception e) {}
setGraphic(null);
}
#Override
public void updateItem(Object item, boolean empty) {
super.updateItem(item, empty);
if (empty) {
setText(null);
setGraphic(null);
} else if (isEditing()) {
if (textField != null) {
textField.setText(getString());
}
setText(null);
setGraphic(textField);
} else {
setText(getString());
setGraphic(null);
//not applicable but shows how to align columns
if (getTableColumn().getText().equals("Amount"))
setAlignment(Pos.CENTER_RIGHT);
}
}
private void createTextField() {
textField = new TextField(getString());
//setPadding(Insets.EMPTY);//screws up table cell for after
textField.setPadding(new Insets(0,0,0,5));
textField.setPrefHeight(this.getHeight()-2);//border hgt??
textField.focusedProperty().addListener(
(ObservableValue<? extends Boolean> arg0, Boolean arg1, Boolean arg2) -> {
if (!arg2) commitEdit(textField.getText());
});
textField.setOnKeyReleased((KeyEvent t) -> {
if (t.getCode() == KeyCode.ENTER) {
commitEdit(textField.getText());
EditingCell.this.getTableView().requestFocus();//why does it lose focus??
EditingCell.this.getTableView().getSelectionModel().selectBelowCell();
}
else if (t.getCode() == KeyCode.RIGHT) {//dont really like this
if (textField.getCaretPosition() >= textField.getLength()){
commitEdit(textField.getText());
EditingCell.this.getTableView().requestFocus();//why does it lose focus??
EditingCell.this.getTableView().getSelectionModel().selectRightCell();
}
}
else if (t.getCode() == KeyCode.UP) {//moves caret to 0
if (textField.getCaretPosition() == 0){
commitEdit(textField.getText());
EditingCell.this.getTableView().requestFocus();//why does it lose focus??
EditingCell.this.getTableView().getSelectionModel().selectAboveCell();
}
}
else if (t.getCode() == KeyCode.DOWN) {
if (textField.getCaretPosition() >= textField.getLength()){
commitEdit(textField.getText());
EditingCell.this.getTableView().requestFocus();//why does it lose focus??
EditingCell.this.getTableView().getSelectionModel().selectBelowCell();
}
}
else if (t.getCode() == KeyCode.ESCAPE) {
cancelEdit();
}
});
textField.addEventFilter(KeyEvent.KEY_RELEASED, (KeyEvent t) -> {
if (t.getCode() == KeyCode.DELETE) {
t.consume();//stop from deleting line in table keyevent
}
});
}
private String getString() {
return getItem() == null ? "" : getItem().toString();
}
}
}
Related
I have method that make a TableColumn editable that I made according to this tutorial, with the two changes recommended in this question witch are changing focusedProperty with setOnAction and add binding. The method is like this:
public static <T> void setAutoCompleteTableColumn(TableColumn<T,String> column, BiConsumer<T, String> field, String type, BiConsumer handleUpdates, List autoComplete){
column.setCellFactory(param -> {
return new TableCell<T, String>(){
private TextField textField;
#Override
public void startEdit() {
super.startEdit();
createTextField();
setText(null);
setGraphic(textField);
textField.selectAll();
}
#Override
public void cancelEdit() {
super.cancelEdit();
setText((String) getItem());
setGraphic(null);
}
#Override
protected void updateItem(String item, boolean empty) {
super.updateItem(item, empty);
if (empty) {
setText(null);
setGraphic(null);
} else {
if (isEditing()) {
if (textField != null) {
textField.setText(getString());
}
setText(null);
setGraphic(textField);
} else {
setText(getString());
setGraphic(null);
}
}
}
private void createTextField(){
textField = new TextField(getString());
textField.setMinWidth(this.getWidth() - this.getGraphicTextGap()* 2);
textField.setOnAction(event ->{
if(textField.getText() != ""){
commitEdit(textField.getText());
}
});
AutoCompletionBinding<T> binding = TextFields.bindAutoCompletion(textField,autoComplete);
}
private String getString() {
return getItem() == null ? "" : getItem().toString();
}
};
});
column.setOnEditCommit(event -> {
T model = event.getRowValue();
if (!event.getNewValue().isEmpty()) {
Cloner cloner = new Cloner();
final T oldModel = cloner.deepClone(model);
//Update the old value with the new value
field.accept(model, event.getNewValue());
//Retrieve changes
handleUpdates.accept(oldModel, model);
}
});
}
I use this method in the controller like this:
TableColumn<Purchase, String> nameColumn = new TableColumn<>();
setAutoCompleteTableColumn(nameColumn, (p,s) -> validatePurchaseEdit(p,s), "text", (o, n) -> updateTracker(o, n), appState.items);
I have a button click event that switch the TableView editable to false.
For some reason when this event is executed the current cell that I am editing does commit edit but an other cell(a random one) switches to edit mode. Can some one explain what is happening, and how can I make all cells commit edit?
I have a custom editable table cell for my table view. Now, the issue I have is that the commitEdit() function gets executed when the table is created. The issue is that it slows down the program as I'm updating items in my database and every single item gets updated.
public class ChoiceBoxCell extends TableCell<Student, Classroom> {
ChoiceBox<Classroom> classroomChoiceBox;
public ChoiceBoxCell(ObservableList<Classroom> classroomObservableList) {
classroomChoiceBox = new ChoiceBox<>();
classroomChoiceBox.setItems(classroomObservableList);
classroomChoiceBox.getSelectionModel().selectedItemProperty().addListener((obs, oldValue, newValue) -> {
if (newValue != null) {
processEdit(newValue);
}
});
}
private void processEdit(Classroom value) {
commitEdit(value);
classroomChoiceBox.setValue(value);
setGraphic(classroomChoiceBox);
}
#Override
public void cancelEdit() {
super.cancelEdit();
setGraphic(classroomChoiceBox);
}
#Override
public void commitEdit(Classroom value) { // gets executed on start up
super.commitEdit(value);
Student student = (Student) getTableRow().getItem();
student.setClassroom(value);
new StudentDao().updateStudent(student); // students get updated for no reason
classroomChoiceBox.setValue(value);
setGraphic(classroomChoiceBox);
}
#Override
public void startEdit() {
super.startEdit();
Classroom value = getItem();
if (value != null) {
classroomChoiceBox.setValue(value);
setGraphic(classroomChoiceBox);
}
}
#Override
protected void updateItem(Classroom item, boolean empty) {
super.updateItem(item, empty);
if (item == null || empty) {
setGraphic(null);
} else {
classroomChoiceBox.setValue(item);
setGraphic(classroomChoiceBox);
}
}
}
EDIT: Solution - thanks to #James_D
classroomChoiceBox.getSelectionModel().selectedItemProperty().addListener((obs, oldValue, newValue) -> {
if (newValue != null && newValue != getItem()) {
processEdit(newValue);
}
});
Solution - thanks to James_D
classroomChoiceBox.getSelectionModel().selectedItemProperty().addListener((obs, oldValue, newValue) -> {
if (newValue != null && newValue != getItem()) {
processEdit(newValue);
}
});
I want to commit changes on ListCell without enter key.
I have the following code :
objetListview1.setEditable(true);
objetListview1.setItems(FXCollections.observableArrayList(observableSet2));
objetListview1.setEditable(true);
objetListview1.setCellFactory(lv -> {
TextFieldListCell<Typ> cell = new TextFieldListCell<Typ>();
StringConverter<Typ> converter = new StringConverter<Typ>() {
#Override
public String toString(Typ obj) {
return obj.getTyp();
}
#Override
public Typ fromString(String string) {
Typ obj = cell.getItem();
if (obj == null) {
Typ newObj = new Typ("");
newObj.setTyp(string);
return newObj;
} else {
obj.setTyp(string);
return obj;
}
}
};
cell.setConverter(converter);
return cell;
});
Actually with this code, I can commit change with enter key.
Any help please?
I found the solution for my problem.
here is my code:
objetListview1.setCellFactory(lv -> new ListCell<Typ>() {
private TextField textField = new TextField() ;
{
textField.setOnAction(e -> {
commitEdit(getItem());
});
textField.focusedProperty().addListener((ObservableValue<? extends Boolean> observable, Boolean oldValue, Boolean newValue) -> {
if (!newValue) {
System.out.println("Commiting " + textField.getText());
commitEdit(getItem());
}
});
}
#Override
protected void updateItem(Typ person, boolean empty) {
super.updateItem(person, empty);
if (empty) {
setText(null);
setGraphic(null);
} else if (isEditing()) {
textField.setText(person.getTyp());
setText(null);
setGraphic(textField);
} else {
setText(person.getTyp());
setGraphic(null);
}
}
#Override
public void startEdit() {
super.startEdit();
textField.setText(getItem().getTyp());
setText(null);
setGraphic(textField);
textField.selectAll();
textField.requestFocus();
}
#Override
public void cancelEdit() {
super.cancelEdit();
setText(getItem().getTyp());
setGraphic(null);
}
#Override
public void commitEdit(Typ person) {
super.commitEdit(person);
person.setTyp(textField.getText());
setText(textField.getText());
setGraphic(null);
}
});
I am using a editable ComboBox cell in a table view. Here is my ComboBox cell class
public class ComboBoxCell extends TableCell<ClassesProperty, String> {
private ComboBox<String> comboBox;
public ComboBoxCell() {
}
#Override
public void startEdit() {
super.startEdit();
if (comboBox == null) {
createComboBox();
}
setGraphic(comboBox);
setContentDisplay(ContentDisplay.GRAPHIC_ONLY);
Platform.runLater(new Runnable() {
#Override
public void run() {
comboBox.requestFocus();
comboBox.getEditor().requestFocus();
comboBox.getEditor().selectAll();
}
});
}
#Override
public void cancelEdit() {
super.cancelEdit();
setText(String.valueOf(getItem()));
setContentDisplay(ContentDisplay.TEXT_ONLY);
}
public void updateItem(String item, boolean empty) {
super.updateItem(item, empty);
if (empty) {
setText(null);
setGraphic(null);
} else {
if (isEditing()) {
if (comboBox != null) {
comboBox.setValue(getString());
}
setGraphic(comboBox);
setContentDisplay(ContentDisplay.GRAPHIC_ONLY);
} else {
setText(getString());
setContentDisplay(ContentDisplay.TEXT_ONLY);
}
}
}
private void createComboBox() {
// ClassesController.getLevelChoice() is the observable list of String
comboBox = new ComboBox<>(ClassesController.getLevelChoice());
comboBox.setEditable(true);
comboBox.setMinWidth(this.getWidth() - this.getGraphicTextGap()*2);
comboBox.setOnKeyPressed(new EventHandler<KeyEvent>() {
#Override
public void handle(KeyEvent t) {
if (t.getCode() == KeyCode.ENTER) {
commitEdit(comboBox.getSelectionModel().getSelectedItem());
} else if (t.getCode() == KeyCode.ESCAPE) {
cancelEdit();
}
}
});
}
private String getString() {
return getItem() == null ? "" : getItem().toString();
}
}
The problem I need to press three clicks on the table cell to get the text field of the combo box to edit the contents. Is there a way to make it in two clicks? I even used Platforn runlater but when I try to edit a cell at first time it takes three mouse clicks but at the second time only two clicks.
In your overridden cell.startEdit() method, Add a ListView with a textfield, then add this listview to setGraphic. This will show the listview directly once the row is selected and the cell is clicked, the listview will be inside the tablecell, I am yet to find a way to make it as a popup
I have defined my own editable cell for a TableView. Everything works great but I want to be able to tab through the cells. Say I'm in cell [2,2] on a tab press I want the selected cell to go to [3,2]. Is this possible? How should I go about achieving this? Should I add a listener to the TableCell or the TableColumn?
Here is my custom TableCell
class EditingCell extends TableCell<Student, String> {
private TextField textField;
public EditingCell(Assignment assign) {
this.setTooltip(new Tooltip(assign.getMaxPoints() + " pts max"));
}
#Override
public void startEdit() {
if (!isEmpty()) {
super.startEdit();
createTextField();
setText(null);
setGraphic(textField);
textField.selectAll();
}
}
#Override
public void cancelEdit() {
super.cancelEdit();
setText((String) getItem());
setGraphic(null);
}
#Override
public void updateItem(String item, boolean empty) {
super.updateItem(item, empty);
if (empty) {
setText(null);
setGraphic(null);
} else {
if (isEditing()) {
if (textField != null) {
textField.setText(getString());
}
setText(null);
setGraphic(textField);
} else {
setText(getString());
setGraphic(null);
}
}
}
private void createTextField() {
textField = new TextField(getString());
textField.setMinWidth(this.getWidth() - this.getGraphicTextGap()* 2);
textField.focusedProperty().addListener(new ChangeListener<Boolean>(){
#Override
public void changed(ObservableValue<? extends Boolean> arg0,
Boolean arg1, Boolean arg2) {
if (!arg2) {
commitEdit(textField.getText());
}
}
});
}
private String getString() {
return getItem() == null ? "" : getItem().toString();
}
}
You can achieve this by adding a KeyListener to your textField. Try adding the following code to your createTextField()
textField.setOnKeyPressed(new EventHandler<KeyEvent>() {
#Override
public void handle(KeyEvent t) {
if (t.getCode() == KeyCode.ENTER) {
commitEdit(Integer.parseInt(textField.getText()));
} else if (t.getCode() == KeyCode.ESCAPE) {
cancelEdit();
} else if (t.getCode() == KeyCode.TAB) {
commitEdit(Integer.parseInt(textField.getText()));
TableColumn nextColumn = getNextColumn(!t.isShiftDown());
if (nextColumn != null) {
getTableView().edit(getTableRow().getIndex(), nextColumn);
}
}
}
});
There are a few more things that you will have to take care of. For a complete example, please follow
https://gist.github.com/abhinayagarwal/9383881