Is there a way to change the text of a TreeItem of TreeView, when I click a button?
I tried to do something like shown in the oracle example
http://docs.oracle.com/javafx/2/ui_controls/tree-view.htm
but I don't want to change the TreeItem by click on it, but rather clicking on the button. In a second step, I want to work with context menu to open a window with a Textfield, where I can manually insert the text to change the treeitems naming.
package treeviewexample;
import javafx.application.Application;
import javafx.event.ActionEvent;
import javafx.event.Event;
import javafx.event.EventHandler;
import javafx.scene.Scene;
import javafx.scene.control.Button;
import javafx.scene.control.TextField;
import javafx.scene.control.TreeCell;
import javafx.scene.control.TreeItem;
import javafx.scene.control.TreeView;
import javafx.scene.input.KeyCode;
import javafx.scene.input.KeyEvent;
import javafx.scene.layout.StackPane;
import javafx.stage.Stage;
import javafx.util.Callback;
public class TreeViewExample extends Application {
#Override
public void start(Stage primaryStage) {
TreeItem root = new TreeItem ("root");
TreeItem item1 = new TreeItem ("Level1");
TreeItem item2 = new TreeItem ("Level1");
TreeItem item11 = new TreeItem ("Level2");
TreeView tree = new TreeView ();
item1.getChildren().add(item11);
tree.setRoot(root);
tree.getRoot().getChildren().addAll(item1, item2);
tree.getRoot().setExpanded(true);
StackPane rootPane = new StackPane();
tree.setEditable(true);
tree.setCellFactory(new Callback<TreeView<String>, TreeCell<String>>(){
#Override
public TreeCell<String> call(TreeView<String> param) {
return new TextFieldTreeCellImpl();
}
});
rootPane.getChildren().add(tree);
Button btn = new Button();
btn.setText("Change Name to 'TEST'");
btn.setOnAction(new EventHandler<ActionEvent>() {
#Override
public void handle(ActionEvent event) {
//CHANGE TEXT OF SELECTED TreeItem to "TEST"?
}
});
rootPane.getChildren().add(btn);
Scene scene = new Scene(rootPane);
primaryStage.setTitle("Hello World!");
primaryStage.setScene(scene);
primaryStage.show();
}
/**
* #param args the command line arguments
*/
public static void main(String[] args) {
launch(args);
}
public class TextFieldTreeCellImpl extends TreeCell<String> {
private TextField textField;
public TextFieldTreeCellImpl (){
}
#Override
public void startEdit() {
super.startEdit();
if (textField == null) {
createTextField();
}
setText(null);
setGraphic(textField);
textField.selectAll();
}
public void cancelEdit() {
super.cancelEdit();
setText((String) getItem());
setGraphic(getTreeItem().getGraphic());
}
#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(getTreeItem().getGraphic());
}
}
};
private void createTextField() {
textField = new TextField(getString());
textField.setOnKeyReleased(new EventHandler<KeyEvent>() {
#Override
public void handle(KeyEvent t) {
if (t.getCode() == KeyCode.ENTER) {
commitEdit(textField.getText());
} else if (t.getCode() == KeyCode.ESCAPE) {
cancelEdit();
}
}
});
}
private String getString() {
return getItem() == null ? "" : getItem().toString();
}
}
}
You can simply use setValue() method to change the text of TreeItem.
btn.setOnAction(new EventHandler<ActionEvent>() {
#Override
public void handle(ActionEvent event) {
desired_tree_item.setValue("TEST");
}
});
Related
I have a TableColumn:
TableColumn<Foo, String> colStatus = new TableColumn("Status");
colStatus.setCellValueFactory(new PropertyValueFactory<>("statusElement"));
On this table I want to apply this cellFactory TextFieldTableCell.forTableColumn() which will make cell editable.
But I also want to combine this one with a custom cellFactory:
colStatus.setCellFactory(new Callback<>() {
public TableCell<Foo, String> call(TableColumn param) {
return new TableCell<>() {
#Override
public void updateItem(String item, boolean empty) {
super.updateItem(item, empty);
if (!isEmpty()){
if(item.equals("error")){
this.setTextFill(Color.RED);
setText(item);
}else{
this.setTextFill(Color.Black);
setText(item);
}
}
}
};
}
});
This cell factory sets text color of cell, based by cell value.
But I don't know how to make the cell editable but also to customize his color, based on value.
Here is MCVE:
#Override
public void start(Stage primaryStage){
List<String> test = new ArrayList<>();
test.add("done(green)");
test.add("done(green)");
test.add("fail(red)");
test.add("done(green)");
TableView<String> tableView = new TableView<>();
tableView.setColumnResizePolicy(TableView.UNCONSTRAINED_RESIZE_POLICY);
tableView.setEditable(true);
TableColumn<String, String> col = new TableColumn<>("Column");
col.setCellValueFactory(data -> new SimpleStringProperty(data.getValue()));
col.setCellFactory(TextFieldTableCell.forTableColumn());
//I want to apply a color based by value from cell
tableView.getColumns().add(col);
tableView.setItems(FXCollections.observableArrayList(test));
primaryStage.setScene(new Scene(tableView));
primaryStage.show();
}
I finally found I can use TextFieldTableCell.forTableColumn() with customization.
Just need to override TextFieldTableCell.
private static class CustomCell extends TextFieldTableCell<String, String>{
#Override
public void updateItem(String item, boolean empty){
super.updateItem(item, empty);
if(item == null || empty) {
setText(null);
return;
}
if(!isEmpty()){
if(item.equals("error")){
this.setTextFill(Color.RED);
setText(item);
}else{
this.setTextFill(Color.BLACK);
setText(item);
}
}
}
}
I was fixed on idea that setCellFactory get as parameter a CallBack.
So I was tried a lot of ways to get a CallBack which returns a TableCell
After I saw the answer of #Sedrick.
I found I can send an lambda implementation like: setCellFactory(e -> new CustomCell()).
Thanks to #Sedrick and #kleopatra.
The key is to extend TableCell as #fabian suggested.
Main
import java.util.ArrayList;
import java.util.List;
import javafx.application.Application;
import javafx.beans.property.SimpleStringProperty;
import javafx.collections.FXCollections;
import javafx.scene.Scene;
import javafx.scene.control.TableCell;
import javafx.scene.control.TableColumn;
import javafx.scene.control.TableView;
import javafx.stage.Stage;
import javafx.util.Callback;
/**
*
* #author blj0011
*/
public class JavaFXTestingGround extends Application
{
/**
* #param args the command line arguments
*/
public static void main(String[] args)
{
launch(args);
}
#Override
public void start(Stage primaryStage) throws Exception
{
List<String> test = new ArrayList<>();
test.add("done(green)");
test.add("done(green)");
test.add("fail(red)");
test.add("done(green)");
TableView<String> tableView = new TableView<>();
tableView.setColumnResizePolicy(TableView.UNCONSTRAINED_RESIZE_POLICY);
tableView.setEditable(true);
TableColumn<String, String> col = new TableColumn<>("Column");
col.setCellValueFactory(data -> new SimpleStringProperty(data.getValue()));
col.setCellFactory((param) -> new CustomCellFactory());
//I want to apply a color based by value from cell
tableView.getColumns().add(col);
tableView.setItems(FXCollections.observableArrayList(test));
primaryStage.setScene(new Scene(tableView));
primaryStage.show();
}
}
CustomCellFactory
import javafx.application.Platform;
import javafx.scene.control.ContentDisplay;
import javafx.scene.control.TableCell;
import javafx.scene.control.TextField;
import javafx.scene.input.KeyCode;
import javafx.scene.paint.Color;
import javafx.scene.text.Text;
/**
*
* #author blj0011
*/
public class CustomCellFactory<T> extends TableCell<T, String>
{
TextField textField = new TextField();
Text text = new Text();
public CustomCellFactory()
{
textField.setOnKeyPressed(keyEvent -> {
if (keyEvent.getCode() == KeyCode.ENTER) {
commitEdit(textField.getText());
}
});
}
#Override
public void commitEdit(String newValue)
{
super.commitEdit(newValue);
setContentDisplay(ContentDisplay.GRAPHIC_ONLY);
}
#Override
public void startEdit()
{
super.startEdit();
if (!isEmpty()) {
setGraphic(textField);
setContentDisplay(ContentDisplay.GRAPHIC_ONLY);
textField.setText(text.getText());
Platform.runLater(() -> textField.requestFocus());
}
}
#Override
public void cancelEdit()
{
super.cancelEdit();
setContentDisplay(ContentDisplay.TEXT_ONLY);
}
#Override
protected void updateItem(String item, boolean empty)
{
super.updateItem(item, empty);
if (item == null || empty) {
setText(null);
}
else {
if (item.equals("error")) {
text.setFill(Color.RED);
}
else {
text.setFill(Color.BLACK);
}
text.setText(item);
setGraphic(text);
}
}
}
Update: I found This while failing to live up to #kleopatra suggestion.
Main
import java.util.ArrayList;
import java.util.List;
import javafx.application.Application;
import javafx.beans.property.SimpleStringProperty;
import javafx.collections.FXCollections;
import javafx.scene.Scene;
import javafx.scene.control.TableCell;
import javafx.scene.control.TableColumn;
import javafx.scene.control.TableView;
import javafx.stage.Stage;
import javafx.util.Callback;
/**
*
* #author blj0011
*/
public class JavaFXTestingGround extends Application
{
/**
* #param args the command line arguments
*/
public static void main(String[] args)
{
launch(args);
}
#Override
public void start(Stage primaryStage) throws Exception
{
List<String> test = new ArrayList<>();
test.add("done(green)");
test.add("done(green)");
test.add("fail(red)");
test.add("done(green)");
TableView<String> tableView = new TableView<>();
tableView.setColumnResizePolicy(TableView.UNCONSTRAINED_RESIZE_POLICY);
tableView.setEditable(true);
TableColumn<String, String> col = new TableColumn<>("Column");
col.setCellValueFactory(data -> new SimpleStringProperty(data.getValue()));
col.setCellFactory(column -> EditCell.createStringEditCell());
//I want to apply a color based by value from cell
tableView.getColumns().add(col);
tableView.setItems(FXCollections.observableArrayList(test));
primaryStage.setScene(new Scene(tableView));
primaryStage.show();
}
}
EditCell
import javafx.event.Event;
import javafx.scene.control.ContentDisplay;
import javafx.scene.control.TableCell;
import javafx.scene.control.TableColumn;
import javafx.scene.control.TableColumn.CellEditEvent;
import javafx.scene.control.TablePosition;
import javafx.scene.control.TableView;
import javafx.scene.control.TextField;
import javafx.scene.input.KeyEvent;
import javafx.scene.paint.Color;
import javafx.util.StringConverter;
public class EditCell<S, T> extends TableCell<S, T>
{
// Text field for editing
// TODO: allow this to be a plugable control.
private final TextField textField = new TextField();
// Converter for converting the text in the text field to the user type, and vice-versa:
private final StringConverter<T> converter;
public EditCell(StringConverter<T> converter)
{
this.converter = converter;
itemProperty().addListener((obx, oldItem, newItem) -> {
if (newItem == null) {
setText(null);
}
else {
setText(converter.toString(newItem));
}
});
setGraphic(textField);
setContentDisplay(ContentDisplay.TEXT_ONLY);
textField.setOnAction(evt -> {
commitEdit(this.converter.fromString(textField.getText()));
});
textField.focusedProperty().addListener((obs, wasFocused, isNowFocused) -> {
if (!isNowFocused) {
commitEdit(this.converter.fromString(textField.getText()));
}
});
textField.addEventFilter(KeyEvent.KEY_PRESSED, event -> {
if (null != event.getCode()) {
switch (event.getCode()) {
case ESCAPE:
textField.setText(converter.toString(getItem()));
cancelEdit();
event.consume();
break;
case RIGHT:
getTableView().getSelectionModel().selectRightCell();
event.consume();
break;
case LEFT:
getTableView().getSelectionModel().selectLeftCell();
event.consume();
break;
case UP:
getTableView().getSelectionModel().selectAboveCell();
event.consume();
break;
case DOWN:
getTableView().getSelectionModel().selectBelowCell();
event.consume();
break;
default:
break;
}
}
});
}
/**
* Convenience converter that does nothing (converts Strings to themselves
* and vice-versa...).
*/
public static final StringConverter<String> IDENTITY_CONVERTER = new StringConverter<String>()
{
#Override
public String toString(String object)
{
return object;
}
#Override
public String fromString(String string)
{
return string;
}
};
/**
* Convenience method for creating an EditCell for a String value.
*
* #param <S>
* #return
*/
public static <S> EditCell<S, String> createStringEditCell()
{
return new EditCell<>(IDENTITY_CONVERTER);
}
// set the text of the text field and display the graphic
#Override
public void startEdit()
{
super.startEdit();
textField.setText(converter.toString(getItem()));
setContentDisplay(ContentDisplay.GRAPHIC_ONLY);
textField.requestFocus();
}
// revert to text display
#Override
public void cancelEdit()
{
super.cancelEdit();
setContentDisplay(ContentDisplay.TEXT_ONLY);
}
// commits the edit. Update property if possible and revert to text display
#Override
public void commitEdit(T item)
{
// This block is necessary to support commit on losing focus, because the baked-in mechanism
// sets our editing state to false before we can intercept the loss of focus.
// The default commitEdit(...) method simply bails if we are not editing...
if (!isEditing() && !item.equals(getItem())) {
TableView<S> table = getTableView();
if (table != null) {
TableColumn<S, T> column = getTableColumn();
CellEditEvent<S, T> event = new CellEditEvent<>(table,
new TablePosition<>(table, getIndex(), column),
TableColumn.editCommitEvent(), item);
Event.fireEvent(column, event);
}
}
if (item.equals("error")) {
setTextFill(Color.RED);
}
else {
setTextFill(Color.BLACK);
}
super.commitEdit(item);
setContentDisplay(ContentDisplay.TEXT_ONLY);
}
}
import javafx.geometry.Pos;
import javafx.scene.control.TableCell;
import javafx.scene.control.TableColumn;
import javafx.scene.control.cell.TextFieldTableCell;
import javafx.scene.layout.*;
import javafx.scene.paint.Color;
import javafx.util.Callback;
import javafx.util.StringConverter;
class className {
private void initColumnTableValue() {
TableColumn<User, Number> columnId = new TableColumn<>("ID");
columnId.setCellFactory(getTextFieldCellFactoryId());
}
/*
* Returns the factory to a cell with a text field for editing
* but with the ability to customize cells
*/
private Callback<TableColumn<User, Number>, TableCell<User, Number>> getTextFieldCellFactoryId() {
return CustomTextFieldTableCell.<User, Number>forTableColumn(
new StringConverter<Number>() {
#Override
public String toString(Number number) {
return number == null ? "" : number.toString();
}
#Override
public Number fromString(String s) {
try {
return Integer.valueOf(s);
} catch (Exception e) {
return null;
}
}
}
);
}
}
/**
* Creates a changed factory for the production of editable cells,
* but with the ability to customize cells
*/
public class CustomTextFieldTableCell extends TextFieldTableCell<User, Number> {
public CustomTextFieldTableCell(StringConverter<Number> var0) {super(var0);}
{ setAlignment(Pos.TOP_CENTER); }
#Override
public void updateItem(Number value, boolean empty){
super.updateItem(value, empty);
if (value == null || empty)
setText("");
else {
if ((int)value % 2 == 0)
setBorder(new Border(new BorderStroke(Color.BLUE, BorderStrokeStyle.DASHED,
new CornerRadii(4), new BorderWidths(1))));
setText(value.toString());
}
}
public static <User, Number> Callback<TableColumn<User, Number>, TableCell<User, Number>> forTableColumn(StringConverter<Number> var0) {
return new Callback<TableColumn<User, Number>, TableCell<User, Number>>() {
#Override
public TableCell<User, Number> call(TableColumn<User, Number> var1) {
return (TableCell<User, Number>) new CustomTextFieldTableCell((StringConverter<java.lang.Number>) var0);
}
};
}
}
I have a ListView with the languages one employee can speak. It uses custom ListCells implemented with the help of setCellFactory. I have a ListCellFactory class where I stored the call for my custom ListCell. In my LanguageListCell (my custom ListCell class) I have a ContextMenu in which I have a MenuItem. The MenuItem fires an event to edit the selected ListCell. The only problem I have encountered in my project is having this double click editing. Whenever I click more than once (when the Cell is not selected) or once (when the Cell is selected) the startEdit gets called. What I want to accomplish is remove this double click editing. But what I have managed to write as a code causes too many problems. For example, when I click on the TextField which is used for the editing, the cancelEdit method is called. And, basically, I can't even click on the TextField without removing it.
See my code for reference
This is the LanguageListCell class
package application;
import javafx.beans.binding.Bindings;
import javafx.event.EventHandler;
import javafx.scene.control.ContextMenu;
import javafx.scene.control.ListCell;
import javafx.scene.control.ListView;
import javafx.scene.control.MenuItem;
import javafx.scene.control.TextField;
import javafx.scene.input.KeyCode;
import javafx.scene.input.KeyEvent;
import javafx.scene.input.MouseButton;
import javafx.scene.input.MouseEvent;
public class LanguageListCell extends ListCell<String> {
private TextField textField;
LanguageListCell cell = this;
int i = 0;
public LanguageListCell(ListView<String> languages) {
ContextMenu contextMenu = new ContextMenu();
cell.setEditable(true);
MenuItem editItem = new MenuItem();
editItem.textProperty().bind(Bindings.format("Edit \"%s\"", cell.itemProperty()));
editItem.setOnAction(event -> {
languages.edit(cell.getIndex());
//cell.startEdit();
});
contextMenu.getItems().add(editItem);
cell.textProperty().bind(cell.itemProperty());
cell.emptyProperty().addListener((obs, wasEmpty, isNowEmpty) -> {
if (isNowEmpty) {
cell.setContextMenu(null);
} else {
if (getString() != "Add") {
cell.setContextMenu(contextMenu);
}
}
});
//This is what I have tried but i get the issue with cancelEdit
//where when I press the TextField it cancels the editing
cell.addEventFilter(MouseEvent.MOUSE_PRESSED, (MouseEvent e) -> {
if (e.getButton().equals(MouseButton.PRIMARY)) {
if (cell.isSelected() && e.getClickCount() >= 1) {
languages.getSelectionModel().clearSelection();
languages.getSelectionModel().select(cell.getItem());
e.consume();
}
if (e.getClickCount() > 1) {
e.consume();
}
}
});
}
public String getString() {
return getItem() == null ? "" : getItem().toString();
}
#Override
public void startEdit() {
super.startEdit();
if (textField == null) {
createTextField();
}
setText(null);
setGraphic(textField);
textField.selectAll();
textField.requestFocus();
}
#Override
public void cancelEdit() {
super.cancelEdit();
setGraphic(null);
setText(getItem());
textField = null;
}
#Override
public void updateItem(String item, boolean empty) {
super.updateItem(item, empty);
cell.textProperty().unbind();
if (empty) {
setText(null);
setGraphic(null);
} else {
if (isEditing()) {
if (textField != null) {
textField.setText(getString());
}
setText(null);
setGraphic(textField);
} else {
setText(getString());
setGraphic(getGraphic());
}
}
}
private void createTextField() {
textField = new TextField(getString());
textField.setOnKeyReleased(new EventHandler<KeyEvent>() {
#Override
public void handle(KeyEvent t) {
if (t.getCode() == KeyCode.ENTER) {
if (textField.getText().trim() != "" && textField.getText().trim().length() > 3) {
commitEdit(textField.getText());
setGraphic(null);
setGraphic(getGraphic());
}
} else if (t.getCode() == KeyCode.ESCAPE) {
cancelEdit();
}
}
});
}
}
This is the LanguageCellFactory class
package application;
import javafx.beans.binding.Bindings;
import javafx.scene.Node;
import javafx.scene.control.ContextMenu;
import javafx.scene.control.ListCell;
import javafx.scene.control.ListView;
import javafx.scene.control.MenuItem;
import javafx.scene.layout.Pane;
import javafx.util.Callback;
import application.LanguageListCell;
public class LanguageCellFactory implements Callback<ListView<String>, ListCell<String>> {
public LanguageCellFactory()
{
}
#Override
public ListCell<String> call(ListView<String> languages)
{
return new LanguageListCell(languages);
}
}
And this is the Main class
package application;
import javafx.application.Application;
import javafx.stage.Stage;
import javafx.scene.Scene;
import javafx.scene.layout.Pane;
import javafx.scene.control.ListView;
import java.util.ArrayList;
import application.LanguageCellFactory;
public class Main extends Application {
private ListView<String> languages;
private Stage stage;
#Override
public void init()
{
}
public void start(Stage primaryStage) {
try {
Pane root = new Pane();
Scene scene = new Scene(root,800,600);
ArrayList<String> list_items = new ArrayList<String>();
list_items.add("Russian");
list_items.add("English");
languages = new ListView<String>();
languages.relocate(150, 62);
languages.getItems().addAll(list_items);
root.getChildren().add(languages);
//languages.setEditable(true);
languages.getSelectionModel().setSelectionMode(SelectionMode.MULTIPLE);
languages.setCellFactory(new LanguageCellFactory());
primaryStage.setScene(scene);
primaryStage.setTitle("First JavaFX App");
primaryStage.show();
} catch(Exception e) {
e.printStackTrace();
}
}
public static void main(String[] args) {
launch(args);
}
}
If someone wants to know how to fix this, I tried removing the editing events for the ListCell and provided my own "editing" behaviour for the cells and now it is working
Here is the modified version of the LanguageListCell class
package application;
import javafx.beans.binding.Bindings;
import javafx.beans.value.ChangeListener;
import javafx.beans.value.ObservableValue;
import javafx.event.EventHandler;
import javafx.scene.Group;
import javafx.scene.control.ContextMenu;
import javafx.scene.control.ListCell;
import javafx.scene.control.ListView;
import javafx.scene.control.MenuItem;
import javafx.scene.control.TextField;
import javafx.scene.input.KeyCode;
import javafx.scene.input.KeyEvent;
import javafx.scene.input.MouseButton;
import javafx.scene.input.MouseEvent;
public class LanguageListCell extends ListCell<String> {
private TextField textField;
LanguageListCell cell = this;
int i = 0;
private boolean textfield_state = false;
ContextMenu contextMenu = new ContextMenu();
MenuItem editItem = new MenuItem();
public LanguageListCell(ListView<String> languages) {
editItem.textProperty().bind(Bindings.format("Edit \"%s\"", cell.itemProperty()));
contextMenu.getItems().add(editItem);
cell.textProperty().bind(cell.itemProperty());
cell.emptyProperty().addListener((obs, wasEmpty, isNowEmpty) -> {
if (isNowEmpty) {
cell.setContextMenu(null);
} else {
if (getString() != "Add") {
cell.setContextMenu(contextMenu);
}
}
});
}
public String getString() {
return getItem() == null ? "" : getItem().toString();
}
#Override
public void updateItem(String item, boolean empty) {
super.updateItem(item, empty);
cell.textProperty().unbind();
//String thecellValue = getString();
if (empty) {
setText(null);
setGraphic(null);
} else {
if (textfield_state) {
if (textField != null) {
textField.setText(getString());
}
setText(null);
setGraphic(textField);
} else {
setText(getString());
setGraphic(getGraphic());
editItem.setOnAction(event -> {
textfield_state = true;
if(textField == null)
{
createTextField();
}
setText(null);
setGraphic(textField);
textField.requestFocus();
textField.selectAll();
});
}
}
}
private void createTextField() {
textField = new TextField(getString());
String CellValue = getString();
textField.setOnKeyReleased(new EventHandler<KeyEvent>() {
#Override
public void handle(KeyEvent t) {
if (t.getCode() == KeyCode.ENTER) {
if (textField.getText().trim() != "" && textField.getText().trim().length() > 3) {
setText(textField.getText().trim());
setGraphic(null);
}
} else if (t.getCode() == KeyCode.ESCAPE) {
//cancelEdit();
setGraphic(null);
setText(CellValue);
textField = null;
}
}
});
textField.focusedProperty().addListener((obs, isNotFocused, isFocused) -> {
if(isFocused)
{
LanguageListCell parent = (LanguageListCell) textField.getParent();
ListView<String> list = parent.getListView();
list.getSelectionModel().clearSelection();
list.getSelectionModel().select(parent.getIndex());
}
else
{
setGraphic(null);
setText(CellValue);
textField = null;
}
});
}
}
I have TreeTableView with 2 columns, so I want to be able something like that:
user double click in cell -> Someclass.getType() returns type of editing field ->in cell I see this type of editing field(TextField or ChoiceBox)
wnen I need to use TextField only, i can use someshing like that
TreeColumn1.setCellFactory(TextFieldTreeTableCell.forTreeTableColumn());
TreeColumn1.setOnEditCommit(firstColumnCommitHandler);
commitHandler:
private EventHandler<TreeTableColumn.CellEditEvent<SomeClass, String>> firstColumnCommitHandler = event -> {
final SomeClass item = event.getRowValue().getValue();
item.setVariable(event.getNewValue());
};
but i need different types, and have no idea howto do this
For this you need to implement the table cell yourself, and display the appropriate components when you go in and out of editing state. Here's a basic idea. The ChoiceBoxs look odd, you may need to work with some CSS to get them looking correct. In this example, if the box in the first column is checked, the second column will use a ChoiceBox for editing; otherwise it will use a TextField.
import java.util.function.Function;
import java.util.stream.IntStream;
import javafx.application.Application;
import javafx.beans.property.BooleanProperty;
import javafx.beans.property.SimpleBooleanProperty;
import javafx.beans.property.SimpleStringProperty;
import javafx.beans.property.StringProperty;
import javafx.beans.value.ObservableValue;
import javafx.scene.Scene;
import javafx.scene.control.ChoiceBox;
import javafx.scene.control.ContentDisplay;
import javafx.scene.control.TableCell;
import javafx.scene.control.TableColumn;
import javafx.scene.control.TableView;
import javafx.scene.control.TextField;
import javafx.scene.control.cell.CheckBoxTableCell;
import javafx.scene.layout.BorderPane;
import javafx.stage.Stage;
public class TableWithVaryingEditor extends Application {
#Override
public void start(Stage primaryStage) {
TableView<Item> table = new TableView<>();
table.setEditable(true);
IntStream.rangeClosed(1, 20).mapToObj(i -> new Item("Item "+i)).forEach(table.getItems()::add);
TableColumn<Item, Boolean> fixedCol = column("Fixed", Item::fixedProperty);
table.getColumns().add(fixedCol);
fixedCol.setCellFactory(CheckBoxTableCell.forTableColumn(fixedCol));
TableColumn<Item, String> nameCol = column("Name", Item::nameProperty);
table.getColumns().add(nameCol);
nameCol.setCellFactory(col -> new TableCell<Item, String>() {
private TextField textField = new TextField();
private ChoiceBox<String> choice = new ChoiceBox<>();
private boolean ignoreChoiceBoxChange = false ;
// anonymous constructor:
{
choice.valueProperty().addListener((obs, oldValue, newValue) -> {
if (! ignoreChoiceBoxChange) {
commitEdit(newValue);
}
});
choice.focusedProperty().addListener((obs, wasFocused, isNowFocused) -> {
if (! isNowFocused) {
cancelEdit();
}
});
choice.showingProperty().addListener((obs, wasShowing, isNowShowing) -> {
if (! isNowShowing) {
cancelEdit();
}
});
textField.setOnAction(e -> commitEdit(textField.getText()));
textField.focusedProperty().addListener((obs, wasFocused, isNowFocused) -> {
if (! isNowFocused) {
cancelEdit();
}
});
}
#Override
public void updateItem(String item, boolean empty) {
super.updateItem(item, empty);
if (isEditing()) {
updateEditor();
} else {
updateText();
}
}
#Override
public void startEdit() {
super.startEdit();
updateEditor();
}
#Override
public void cancelEdit() {
super.cancelEdit();
updateText();
}
#Override
public void commitEdit(String item) {
super.commitEdit(item);
updateText();
}
private void updateEditor() {
setContentDisplay(ContentDisplay.GRAPHIC_ONLY);
int index = getIndex();
Item item = getTableView().getItems().get(index);
if (item.isFixed()) {
ignoreChoiceBoxChange = true ;
choice.getItems().setAll(getItem(), "Choice 1", "Choice 2");
choice.getSelectionModel().select(getItem());
setGraphic(choice);
choice.show();
ignoreChoiceBoxChange = false ;
} else {
textField.setText(getItem());
setGraphic(textField);
}
}
private void updateText() {
setContentDisplay(ContentDisplay.TEXT_ONLY);
if (isEmpty()) {
setText(null);
} else {
setText(getItem());
}
}
});
primaryStage.setScene(new Scene(new BorderPane(table), 600, 400));
primaryStage.show();
}
private <S,T> TableColumn<S,T> column(String title, Function<S, ObservableValue<T>> property) {
TableColumn<S,T> col = new TableColumn<>(title);
col.setCellValueFactory(cellData -> property.apply(cellData.getValue()));
return col ;
}
public static class Item {
private final BooleanProperty fixed = new SimpleBooleanProperty();
private final StringProperty name = new SimpleStringProperty();
public Item(String name) {
setName(name);
}
public final BooleanProperty fixedProperty() {
return this.fixed;
}
public final boolean isFixed() {
return this.fixedProperty().get();
}
public final void setFixed(final boolean fixed) {
this.fixedProperty().set(fixed);
}
public final StringProperty nameProperty() {
return this.name;
}
public final String getName() {
return this.nameProperty().get();
}
public final void setName(final String name) {
this.nameProperty().set(name);
}
}
public static void main(String[] args) {
launch(args);
}
}
I'm using the following code from Oracle:
import java.util.Arrays;
import java.util.List;
import javafx.application.Application;
import javafx.event.Event;
import javafx.event.EventHandler;
import javafx.scene.Node;
import javafx.scene.Scene;
import javafx.scene.control.TextField;
import javafx.scene.control.TreeCell;
import javafx.scene.control.TreeItem;
import javafx.scene.control.TreeView;
import javafx.scene.image.Image;
import javafx.scene.image.ImageView;
import javafx.scene.input.KeyCode;
import javafx.scene.input.KeyEvent;
import javafx.scene.paint.Color;
import javafx.stage.Stage;
import javafx.util.Callback;
import javafx.beans.property.SimpleStringProperty;
import javafx.scene.control.ContextMenu;
import javafx.scene.control.MenuItem;
import javafx.scene.layout.VBox;
public class TreeViewSample extends Application {
List<Employee> employees = Arrays.<Employee>asList(
new Employee("Ethan Williams", "Sales Department"),
new Employee("Emma Jones", "Sales Department"),
new Employee("Michael Brown", "Sales Department"),
new Employee("Anna Black", "Sales Department"),
new Employee("Rodger York", "Sales Department"),
new Employee("Susan Collins", "Sales Department"),
new Employee("Mike Graham", "IT Support"),
new Employee("Judy Mayer", "IT Support"),
new Employee("Gregory Smith", "IT Support"),
new Employee("Jacob Smith", "Accounts Department"),
new Employee("Isabella Johnson", "Accounts Department"));
TreeItem<String> rootNode =
new TreeItem<String>("MyCompany Human Resources");
public static void main(String[] args) {
Application.launch(args);
}
#Override
public void start(Stage stage) {
rootNode.setExpanded(true);
for (Employee employee : employees) {
TreeItem<String> empLeaf = new TreeItem<String>(employee.getName());
boolean found = false;
for (TreeItem<String> depNode : rootNode.getChildren()) {
if (depNode.getValue().contentEquals(employee.getDepartment())){
depNode.getChildren().add(empLeaf);
found = true;
break;
}
}
if (!found) {
TreeItem depNode = new TreeItem(employee.getDepartment());
rootNode.getChildren().add(depNode);
depNode.getChildren().add(empLeaf);
}
}
stage.setTitle("Tree View Sample");
VBox box = new VBox();
final Scene scene = new Scene(box, 400, 300);
scene.setFill(Color.LIGHTGRAY);
TreeView<String> treeView = new TreeView<String>(rootNode);
treeView.setEditable(true);
treeView.setCellFactory(new Callback<TreeView<String>,TreeCell<String>>(){
#Override
public TreeCell<String> call(TreeView<String> p) {
return new TextFieldTreeCellImpl();
}
});
box.getChildren().add(treeView);
stage.setScene(scene);
stage.show();
}
private final class TextFieldTreeCellImpl extends TreeCell<String> {
private TextField textField;
private ContextMenu addMenu = new ContextMenu();
public TextFieldTreeCellImpl() {
MenuItem addMenuItem = new MenuItem("Add Employee");
addMenu.getItems().add(addMenuItem);
addMenuItem.setOnAction(new EventHandler() {
public void handle(Event t) {
TreeItem newEmployee =
new TreeItem<String>("New Employee");
getTreeItem().getChildren().add(newEmployee);
}
});
}
#Override
public void startEdit() {
super.startEdit();
if (textField == null) {
createTextField();
}
setText(null);
setGraphic(textField);
textField.selectAll();
}
#Override
public void cancelEdit() {
super.cancelEdit();
setText((String) getItem());
setGraphic(getTreeItem().getGraphic());
}
#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(getTreeItem().getGraphic());
if (
!getTreeItem().isLeaf()&&getTreeItem().getParent()!= null
){
setContextMenu(addMenu);
}
}
}
}
private void createTextField() {
textField = new TextField(getString());
textField.setOnKeyReleased(new EventHandler<KeyEvent>() {
#Override
public void handle(KeyEvent t) {
if (t.getCode() == KeyCode.ENTER) {
commitEdit(textField.getText());
} else if (t.getCode() == KeyCode.ESCAPE) {
cancelEdit();
}
}
});
}
private String getString() {
return getItem() == null ? "" : getItem().toString();
}
}
public static class Employee {
private final SimpleStringProperty name;
private final SimpleStringProperty department;
private Employee(String name, String department) {
this.name = new SimpleStringProperty(name);
this.department = new SimpleStringProperty(department);
}
public String getName() {
return name.get();
}
public void setName(String fName) {
name.set(fName);
}
public String getDepartment() {
return department.get();
}
public void setDepartment(String fName) {
department.set(fName);
}
}
}
This code produces a GUI with a very basic, editable TreeView. However, when clicking around the cells that populate the tree, eventually, the text fields used for editing will begin to display incorrect information (information that is contained within the tree, but that is not represented by the cell being edited). I do not understand why this is happening, and I haven't found any reference of this happening anywhere on Google or elsewhere on StackOverflow. If anyone could help me understand why this is occuring, I'd be very happy.
Thanks!
I checked your application and I have found only one weird thing. When you create the edit textfield once, the unsaved information will be visible in that textfield later.
This modification will solve it:
# Override
public void cancelEdit() {
super.cancelEdit();
this.setText(this.getItem());
this.textField.setText(this.getItem());
this.setGraphic(this.getTreeItem().getGraphic());
}
I have a problem on JavaFx TableView with large data such as whats happened on the items on the picture.
With huge data,the TableView do crazy works when i select a row or a cell.
I repeated the Number '131004' more than once for reference only
How can i solve this problem with large data?!? i didn't find any resource for that.
EDITED
also i have the same problem when i use scroll to up/down
The class is
import javafx.application.Platform;
import javafx.event.EventHandler;
import javafx.geometry.Pos;
import javafx.scene.control.ContentDisplay;
import javafx.scene.control.TableCell;
import javafx.scene.control.TextField;
import javafx.scene.input.KeyCode;
import javafx.scene.input.KeyEvent;
/**
*
* #author mohammad
*/
public class EditingCellNew extends TableCell<Object, Integer> {
private TextField textField;
public EditingCellNew() {
this.setAlignment(Pos.CENTER);
}
#Override
public void startEdit() {
if (!isEmpty()) {
super.startEdit();
if (textField == null) {
createTextField();
}
textField.setText(getString());
//setText(null);
setGraphic(textField);
setContentDisplay(ContentDisplay.GRAPHIC_ONLY);
textField.selectAll();
Platform.runLater(new Runnable() {
#Override
public void run() {
textField.requestFocus();
}
});
}
}
#Override
public void cancelEdit() {
super.cancelEdit();
setText(String.valueOf(getItem()));
setContentDisplay(ContentDisplay.TEXT_ONLY);
}
#Override
public void updateItem(Integer item, boolean empty) {
super.updateItem(item, empty);
if (empty) {
setText(null);
setGraphic(null);
} else {
if (isEditing()) {
if (textField != null) {
textField.setText(getString());
}
setGraphic(textField);
setContentDisplay(ContentDisplay.GRAPHIC_ONLY);
} else {
setText(getString());
setContentDisplay(ContentDisplay.TEXT_ONLY);
}
}
}
private void createTextField() {
textField = new TextField(getString());
textField.setAlignment(Pos.CENTER);
textField.setMinWidth(this.getWidth() - this.getGraphicTextGap() * 2);
textField.setOnKeyPressed(new EventHandler<KeyEvent>() {
#Override
public void handle(KeyEvent t) {
if (t.getCode() == KeyCode.ENTER) {
commitEdit(Integer.parseInt(textField.getText()));
System.out.println("table row : " + getTableRow().getIndex());
updateTableRow(getTableRow());
updateTableColumn(getTableColumn());
updateTableView(getTableView());
} else if (t.getCode() == KeyCode.ESCAPE) {
cancelEdit();
}
}
});
textField.setOnKeyReleased(new EventHandler<KeyEvent>() {
#Override
public void handle(KeyEvent t) {
if (t.getCode() == KeyCode.ENTER) {
updateTableRow(getTableRow());
updateTableView(getTableView());
//bondTable.getItems().set(bondTable.getSelectionModel().getSelectedIndex(), bondTable.getSelectionModel().getSelectedItem());
System.out.println("realesed enter");
}
}
});
}
private String getString() {
return String.valueOf(getItem()) == null ? "" : String.valueOf(getItem());
}
}