Javafx: Table Column commit on Button click - javafx

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?

Related

CommitEdit function gets executed on creation of the table

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);
}
});

javafx: Custom TableColumn

I want to make a custom TableColumn that when in editable state it's cells will be auto complete TextFields, here is what I tried:
public static <T,S> void setAutoCompleteTableColumn(TableColumn<T,S> column, List items){
column.setCellFactory(param -> {
return new TableCell<T, S>(){
final TextField textField = new TextField();
#Override
protected void updateItem(S item, boolean empty) {
super.updateItem(item, empty);
if(item == null){
setGraphic(null);
}else {
setGraphic(textField);
AutoCompletionBinding<T> binding = TextFields.bindAutoCompletion(textField,items);
binding.setOnAutoCompleted(event ->{
//handleCompleted.accept(event.getCompletion());
});
}
}
};
});
}
This code have produced (obviously) this:
But I want this form only when the TableView is in edit mode, So I did this instead:
public static <T,S> void setAutoCompleteTableColumn(TableColumn<T,S> column, List items){
column.setCellFactory(param -> {
return new TableCell<T, S>(){
final TextField textField = new TextField();
#Override
protected void updateItem(S item, boolean empty) {
super.updateItem(item, empty);
if(item == null){
setGraphic(null);
}else {
editableProperty().addListener((obs, oldValue, newValue)->{
if(newValue){
setGraphic(textField);
}else{
setText(item.toString());
}
});
AutoCompletionBinding<T> binding = TextFields.bindAutoCompletion(textField,items);
binding.setOnAutoCompleted(event ->{
//handleCompleted.accept(event.getCompletion());
});
}
}
};
});
}
As you can see I added a listener to editableProperty, which resulted in nothing.
What I want is an editable TableColumn like in here, but with auto complete TextField. Any idea how to accomplish this?

How to commit changes on a JavaFX ListCell without enter key?

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);
}
});

JavaFX editable ComboBox in a table view

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

JavaFX tab through editable cells

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

Resources