TableView individual cell listener JavaFX - java

So I'm developing a "to-do list" application where there is a TableView containing the tasks to do and in my TableView there is a "deadline" column. I would like that if the deadline date of a task has been passed it makes that specific deadlines date red.
So if the user opens the app on the first of January/2015 and one of his tasks deadline was the 31st of December/2014, its corresponding deadline cell has its text colored in red.
The table is obviously associated to an ObservableList of "Task" objects which have an ObjectProperty "deadline" field.
How would I go about doing this?

Define a CSS PseudoClass to represent an overdue item. Use a cell factory that sets the pseudoclass state according to whether or not the deadline has passed. You didn't post any code, but the following should help:
TableColumn<Task, LocalDate> deadlineColumn = new TableColumn<>("Deadline");
deadlineColumn.setCellValueFactory( cellData -> cellData.getValue().deadlineProperty() ); // or similar...
PseudoClass overdue = PseudoClass.getPseudoClass("overdue");
deadlineColumn.setCellFactory(col -> new TableCell<Task, LocalDate>() {
#Override
public void updateItem(LocalDate deadline, boolean empty) {
super.updateItem(deadline, empty) ;
if (empty) {
pseudoClassStateChanged(overdue, false);
setText(null);
} else {
pseudoClassStateChanged(overdue, LocalDate.now().isAfter(deadline));
setText(deadline.toString());
}
}
});
And then in an external css file you can do
.table-cell:overdue {
-fx-text-fill: red ;
}
Update: here's a complete example, with the CSS shown above in a file called overdue.css:
import java.time.LocalDate;
import java.util.function.Function;
import javafx.application.Application;
import javafx.beans.property.ObjectProperty;
import javafx.beans.property.SimpleObjectProperty;
import javafx.beans.property.SimpleStringProperty;
import javafx.beans.property.StringProperty;
import javafx.beans.value.ObservableValue;
import javafx.css.PseudoClass;
import javafx.scene.Scene;
import javafx.scene.control.TableCell;
import javafx.scene.control.TableColumn;
import javafx.scene.control.TableView;
import javafx.scene.layout.BorderPane;
import javafx.stage.Stage;
public class ToDoTable extends Application {
#Override
public void start(Stage primaryStage) {
TableView<ToDoItem> table = new TableView<>();
table.getColumns().add(createColumn("Name", ToDoItem::nameProperty));
TableColumn<ToDoItem, LocalDate> deadlineCol = createColumn("Deadline", ToDoItem::deadlineProperty);
PseudoClass overdue = PseudoClass.getPseudoClass("overdue");
deadlineCol.setCellFactory(col -> new TableCell<ToDoItem, LocalDate>() {
#Override
public void updateItem(LocalDate deadline, boolean empty) {
super.updateItem(deadline, empty);
if (empty) {
pseudoClassStateChanged(overdue, false);
setText(null);
} else {
pseudoClassStateChanged(overdue, LocalDate.now().isAfter(deadline));
setText(deadline.toString());
}
}
});
table.getColumns().add(deadlineCol);
for (int i=1; i <= 10; i++) {
LocalDate deadline = LocalDate.now().plusDays(i - 5);
ToDoItem item = new ToDoItem("Item "+i, deadline);
table.getItems().add(item);
}
BorderPane root = new BorderPane(table);
Scene scene = new Scene(root, 800, 600);
scene.getStylesheets().add("overdue.css");
primaryStage.setScene(scene);
primaryStage.show();
}
private static <S,T> TableColumn<S,T> createColumn(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 ToDoItem {
private final StringProperty name = new SimpleStringProperty();
private final ObjectProperty<LocalDate> deadline = new SimpleObjectProperty<>();
public ToDoItem(String name, LocalDate deadline) {
this.name.set(name);
this.deadline.set(deadline);
}
public final StringProperty nameProperty() {
return this.name;
}
public final java.lang.String getName() {
return this.nameProperty().get();
}
public final void setName(final java.lang.String name) {
this.nameProperty().set(name);
}
public final ObjectProperty<LocalDate> deadlineProperty() {
return this.deadline;
}
public final java.time.LocalDate getDeadline() {
return this.deadlineProperty().get();
}
public final void setDeadline(final java.time.LocalDate deadline) {
this.deadlineProperty().set(deadline);
}
}
public static void main(String[] args) {
launch(args);
}
}

Related

How to center the text in TreeTableView Column?

I've a JFXTreeTableView and i want to center the text of the data for each column.
there is one of my creating columns code :
JFXTreeTableColumn<TableData, String> DrinkColumn = new JFXTreeTableColumn<>("Drink");
DrinkColumn.setPrefWidth(100);
DrinkColumn.setCellValueFactory(new Callback<TreeTableColumn.CellDataFeatures<TableData, String>, ObservableValue<String>>() {
#Override
public ObservableValue<String> call(TreeTableColumn.CellDataFeatures<TableData, String> param) {
return param.getValue().getValue().Drink;
}
}
);
I don't use JFoenix, but using a standard TreeTableView, the following external CSS will center the text in tree table cells:
.tree-table-cell {
-fx-alignment: center ;
}
Here's a SSCCE (the code above goes in style.css):
import java.util.ArrayList;
import java.util.List;
import java.util.Random;
import javafx.application.Application;
import javafx.beans.property.IntegerProperty;
import javafx.beans.property.SimpleIntegerProperty;
import javafx.beans.property.SimpleStringProperty;
import javafx.beans.property.StringProperty;
import javafx.scene.Scene;
import javafx.scene.control.TreeItem;
import javafx.scene.control.TreeTableColumn;
import javafx.scene.control.TreeTableView;
import javafx.stage.Stage;
public class TreeTableViewTest extends Application {
#Override
public void start(Stage primaryStage) {
TreeTableView<Item> table = new TreeTableView<>();
TreeTableColumn<Item, String> col = new TreeTableColumn<>("Item");
col.setCellValueFactory(cellData -> cellData.getValue().getValue().nameProperty());
col.setPrefWidth(250);
table.getColumns().add(col);
TreeTableColumn<Item, Number> valueCol = new TreeTableColumn<>("Value");
valueCol.setCellValueFactory(cellData -> cellData.getValue().getValue().valueProperty());
valueCol.setPrefWidth(150);
table.getColumns().add(valueCol);
table.setRoot(createRandomTree(50));
Scene scene = new Scene(table);
scene.getStylesheets().add("style.css");
primaryStage.setScene(scene);
primaryStage.show();
}
private TreeItem<Item> createRandomTree(int nItems) {
Random rng = new Random();
TreeItem<Item> root = new TreeItem<>(new Item("Item 1", rng.nextInt(1000)));
root.setExpanded(true);
List<TreeItem<Item>> items = new ArrayList<>();
items.add(root);
for (int i = 2 ; i <= nItems ; i++) {
TreeItem<Item> item = new TreeItem<>(new Item("Item "+i, rng.nextInt(1000)));
item.setExpanded(true);
items.get(rng.nextInt(items.size())).getChildren().add(item);
items.add(item);
}
return root ;
}
public static class Item {
private final StringProperty name = new SimpleStringProperty();
private final IntegerProperty value = new SimpleIntegerProperty();
public Item(String name, int value) {
setName(name);
setValue(value);
}
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 final IntegerProperty valueProperty() {
return this.value;
}
public final int getValue() {
return this.valueProperty().get();
}
public final void setValue(final int value) {
this.valueProperty().set(value);
}
}
public static void main(String[] args) {
launch(args);
}
}
If you want to center only specific columns, then use a cell factory on the column and set a CSS class or PseudoClass on the cell:
valueCol.setCellFactory(column -> {
TreeTableCell<Item, Number> cell = new TreeTableCell<Item, Number>() {
#Override
protected void updateItem(Number value, boolean empty) {
super.updateItem(value, empty);
if (empty) {
setText(null);
} else {
setText(value.toString());
}
}
};
cell.pseudoClassStateChanged(PseudoClass.getPseudoClass("centered"), true);
return cell ;
});
and modify the CSS accordingly:
.tree-table-cell:centered {
-fx-alignment: center ;
}
The latter version gives

Custom CheckBox in TableView

I want to apply a custom style using the setStyle method to the CheckBox of a TableView but I can not do it. I have tried to create a CheckBox inside the setCellFactory regardless of CheckBoxTableCell but in that case the value in the ObservableList<Person> is no longer updated.
My code is the following:
public class Person {
private String name;
private boolean accepted;
public Person(String name, boolean accepted) {
this.name = name;
this.accepted = accepted;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public boolean isAccepted() {
return accepted;
}
public void setAccepted(boolean accepted) {
this.accepted = accepted;
}
}
The main class:
import javafx.application.Application;
import javafx.beans.property.SimpleBooleanProperty;
import javafx.beans.value.ObservableValue;
import javafx.collections.FXCollections;
import javafx.collections.ObservableList;
import javafx.geometry.Insets;
import javafx.geometry.Pos;
import javafx.scene.Scene;
import javafx.scene.control.TableColumn;
import javafx.scene.control.TableColumn.CellDataFeatures;
import javafx.scene.control.TableView;
import javafx.scene.control.cell.CheckBoxTableCell;
import javafx.scene.control.cell.PropertyValueFactory;
import javafx.scene.control.cell.TextFieldTableCell;
import javafx.scene.layout.StackPane;
import javafx.stage.Stage;
public class TableViewWithCheckBox extends Application {
#Override
public void start(Stage stage) {
TableView<Person> table = new TableView<>();
// Editable
table.setEditable(true);
TableColumn<Person, String> fullNameCol = new TableColumn<>("Name");
TableColumn<Person, Boolean> acceptedCol = new TableColumn<>("Accepted");
// NAME
fullNameCol.setCellValueFactory(new PropertyValueFactory<>("name"));
fullNameCol.setCellFactory(TextFieldTableCell.<Person> forTableColumn());
fullNameCol.setMinWidth(200);
// ACCEPTED
acceptedCol.setCellValueFactory((CellDataFeatures<Person, Boolean> param) -> {
Person person = param.getValue();
SimpleBooleanProperty booleanProp = new SimpleBooleanProperty(person.isAccepted());
booleanProp.addListener(
(ObservableValue<? extends Boolean> observable,
Boolean oldValue, Boolean newValue) -> {
person.setAccepted(newValue);
});
return booleanProp;
});
acceptedCol.setCellFactory((TableColumn<Person, Boolean> p) -> {
CheckBoxTableCell<Person, Boolean> cell = new CheckBoxTableCell<>();
cell.setAlignment(Pos.CENTER);
return cell;
});
ObservableList<Person> list = getPersonList();
table.setItems(list);
table.getColumns().addAll(fullNameCol, acceptedCol);
StackPane root = new StackPane();
root.setPadding(new Insets(5));
root.getChildren().add(table);
Scene scene = new Scene(root, 300, 300);
stage.setScene(scene);
stage.show();
}
private ObservableList<Person> getPersonList() {
Person person1 = new Person("John White", true);
Person person2 = new Person("Kevin Land", false);
Person person3 = new Person("Rouse Hill", true);
ObservableList<Person> list = FXCollections.observableArrayList(person1, person2, person3);
return list;
}
}
And the code with a custom CheckBox but that does not update the observable list:
acceptedCol.setCellFactory((TableColumn<Person, Boolean> success) -> {
TableCell<Person, Boolean> cell = new TableCell<Person, Boolean>(){
#Override
public void updateItem(Boolean item, boolean empty) {
super.updateItem(item, empty);
if(empty || item == null){
setGraphic(null);
} else {
CheckBox myCheckBox = new CheckBox();
myCheckBox.getStyleClass().add("myPersonalCheckBoxStyle");
myCheckBox.setSelected(item);
setGraphic(myCheckBox);
}
}
};
return cell;
});
CheckBoxTableCell does all the heavy lifting for you, just need a little help from your Person class which should use/expose properties (vs. plain fields with getters/setters only). Then configure the column with the acceptedProperty and apply a custom style to the cell.
In code snippets:
public class Person {
private BooleanProperty accepted;
public Person(String name, boolean single) {
this.accepted = new SimpleBooleanProperty(single);
...
}
public BooleanProperty acceptedProperty() {
return accepted;
}
public boolean isAccepted() {
return acceptedProperty().get();
}
....
Configuration of the column with cell/Value/Factory:
acceptedCol.setCellValueFactory(new PropertyValueFactory<>("accepted"));
acceptedCol.setCellFactory((TableColumn<Person, Boolean> p) -> {
CheckBoxTableCell<Person, Boolean> cell = new CheckBoxTableCell<>();
cell.getStyleClass().add("custom-cell");
cell.setAlignment(Pos.CENTER);
return cell;
});
Some custom style:
/**
* Just want to see an effect, doesn't matter what
*/
.custom-cell > .check-box {
-fx-background-color: blue;
}
.custom-cell > .check-box:selected {
-fx-background-color: red;
}

JavaFX - how to recognize the position of ScrollBar in a TableView

I have written this little example application:
package application;
import javafx.application.Application;
import javafx.beans.property.SimpleStringProperty;
import javafx.beans.property.StringProperty;
import javafx.event.EventHandler;
import javafx.scene.Scene;
import javafx.scene.control.TableColumn;
import javafx.scene.control.TableColumn.CellEditEvent;
import javafx.scene.control.TableView;
import javafx.scene.control.cell.PropertyValueFactory;
import javafx.scene.control.cell.TextFieldTableCell;
import javafx.scene.input.MouseEvent;
import javafx.scene.layout.StackPane;
import javafx.stage.Stage;
public class Main extends Application {
public class Person {
private StringProperty firstName = new SimpleStringProperty();
private StringProperty lastName = new SimpleStringProperty();
public Person(String firstName, String lastName) {
this.firstName.set(firstName);
this.lastName.set(lastName);
}
public String getFirstName() {
return firstName.get();
}
public String getLastName() {
return lastName.get();
}
public StringProperty firstNameProperty() {
return firstName;
}
public StringProperty lastNameProperty() {
return lastName;
}
}
#Override
public void start(Stage primaryStage) {
try {
StackPane root = new StackPane();
TableView<Person> tv = new TableView<>();
TableColumn<Person, String> col = new TableColumn<Person, String>("FirstName");
col.setCellValueFactory(new PropertyValueFactory<Person, String>("firstName"));
tv.getColumns().add(col);
tv.setEditable(true);
col = new TableColumn<Person, String>("LastName");
col.setCellValueFactory(new PropertyValueFactory<Person, String>("lastName"));
col.setCellFactory(TextFieldTableCell.forTableColumn());
col.setOnEditCommit(new EventHandler<TableColumn.CellEditEvent<Person, String>>() {
#Override
public void handle(CellEditEvent<Person, String> event) {
System.out.println(tv.getItems().get(1).getLastName());
}
});
tv.getColumns().add(col);
for (int i = 0; i < 30; i++) {
tv.getItems().add(new Person("Test" + i, "Test" + i));
}
root.getChildren().add(tv);
Scene scene = new Scene(root, 400, 200);
primaryStage.setScene(scene);
primaryStage.show();
tv.addEventFilter(MouseEvent.MOUSE_RELEASED, event -> {
// ...
});
} catch (Exception e) {
e.printStackTrace();
}
}
public static void main(String[] args) {
launch(args);
}
}
I want to perform action when the ScrollBar has reached the bottom. Then I want to reload more datas from the database. But only then, when the user has seen all the already loaded datas (= scrollbar on the bottom). Do you have nice suggestions to solve this issue?
My first idea was to catch the MOUSE_RELEASED event (when the users drags the bar) of the TableView and then to check the position of the ScrollBar:
- getValue() gets the position of the bar
- getMax() the maximum value (=bottom).
But I can't find a way (without using the css-selector via this method) to get the ScrollBar from a given TableView. So I can't check the position of it in a certain TableView.
Do you have any ideas??
I am excited. Thanks for your help.
The only way to get the scroll bar is via a lookup, which is a bit of a hack, but it will work as long as you do it after the table has been rendered on the scene. You need
ScrollBar verticalBar = (ScrollBar) table.lookup(".scroll-bar:vertical");
Note that there's no need to mess with user events: you can just observe the scroll bar's value property directly:
verticalBar.valueProperty().addListener((obs, oldValue, newValue) -> {
if (newValue.doubleValue() >= verticalBar.getMax()) {
// add more data...
}
});
SSCCE:
import java.util.ArrayList;
import java.util.List;
import java.util.function.Function;
import javafx.application.Application;
import javafx.beans.property.IntegerProperty;
import javafx.beans.property.SimpleIntegerProperty;
import javafx.beans.property.SimpleStringProperty;
import javafx.beans.property.StringProperty;
import javafx.beans.value.ObservableValue;
import javafx.concurrent.Task;
import javafx.scene.Scene;
import javafx.scene.control.ScrollBar;
import javafx.scene.control.TableColumn;
import javafx.scene.control.TableView;
import javafx.scene.layout.BorderPane;
import javafx.stage.Stage;
public class AddMoreTableDataOnScrollToBottom extends Application {
#Override
public void start(Stage primaryStage) {
TableView<Item> table = new TableView<>();
table.getColumns().add(column("Item", Item::nameProperty));
table.getColumns().add(column("Value", Item::valueProperty));
addMoreData(table, 20);
Scene scene = new Scene(new BorderPane(table), 400, 400);
primaryStage.setScene(scene);
primaryStage.show();
ScrollBar verticalBar = (ScrollBar) table.lookup(".scroll-bar:vertical");
verticalBar.valueProperty().addListener((obs, oldValue, newValue) -> {
if (newValue.doubleValue() >= verticalBar.getMax()) {
addMoreData(table, 20);
}
});
}
private void addMoreData(TableView<Item> table, int numItems) {
Task<List<Item>> dataRetrieveTask = new Task<List<Item>>() {
#Override
public List<Item> call() throws Exception {
// mimic connect to db:
Thread.sleep(500);
List<Item> items = new ArrayList<>();
int nextItem = table.getItems().size() + 1 ;
for (int i = nextItem; i < nextItem + numItems; i++ ){
items.add(new Item("Item "+i, i));
}
return items ;
}
};
dataRetrieveTask.setOnSucceeded(e -> table.getItems().addAll(dataRetrieveTask.getValue()));
new Thread(dataRetrieveTask).start();
}
private <S,T> TableColumn<S,T> column(String title, Function<S, ObservableValue<T>> prop) {
TableColumn<S,T> col = new TableColumn<>(title);
col.setCellValueFactory(cellData -> prop.apply(cellData.getValue()));
return col ;
}
public static class Item {
private final StringProperty name = new SimpleStringProperty();
private final IntegerProperty value = new SimpleIntegerProperty();
public Item(String name, int value) {
setName(name);
setValue(value);
}
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 final IntegerProperty valueProperty() {
return this.value;
}
public final int getValue() {
return this.valueProperty().get();
}
public final void setValue(final int value) {
this.valueProperty().set(value);
}
}
public static void main(String[] args) {
launch(args);
}
}

Updating TableView Row Colour with Result From MySQL Query

My users can create jobs that are added to a MySQL database. The jobs have a priority (1, 2 or 3). What I would like to do is modify the colour of individual rows based on the priority of the job, for example prioirty 3 is a red row as this is a more urgent job, priority 1 is a green row as it has a lower urgency.
I have a job model class that has a getter/setter for priority;
public int getPrioritySetting() {
return prioritySetting;
}
public void setPrioritySetting(final int prioritySetting) {
this.prioritySetting = prioritySetting;
}
I have two questions, what is the "easiest" way to get the priority of each inidivual job from the MySQL database and (using this), what is the "easiest" way to modify the appearance of the row? I'm currently using TableView in JavaFX with FXML files built through scenebuilder.
I don't understand the first question: presumably you are getting the Job objects from the database at some point anyway, so you would just populate the prioritySetting field when you do so.
To change the appearance of the row, use a row factory, and set some CSS pseudoclasses
PseudoClass highPriority = PseudoClass.getPseudoClass("high-priority");
PseudoClass lowPriority = PseudoClass.getPseudoClass("low-priority");
table.setRowFactory(tv -> new TableRow<Job>() {
#Override
public void updateItem(Job item, boolean empty) {
super.updateItem(item, empty);
pseudoClassStateChanged(highPriority, item != null && item.getPrioritySetting() == 3);
pseudoClassStateChanged(lowPriority, item != null && item.getPrioritySetting() == 1);
}
});
Then just define whatever style you need in an external CSS file:
.table-row-cell:high-priority {
-fx-background: red ;
}
.table-row-cell:low-priority {
-fx-background: green ;
}
Here is an SSCCE
import java.util.List;
import java.util.Random;
import java.util.function.Function;
import java.util.stream.Collectors;
import java.util.stream.IntStream;
import javafx.application.Application;
import javafx.beans.property.IntegerProperty;
import javafx.beans.property.SimpleIntegerProperty;
import javafx.beans.property.SimpleStringProperty;
import javafx.beans.property.StringProperty;
import javafx.beans.value.ObservableValue;
import javafx.css.PseudoClass;
import javafx.scene.Scene;
import javafx.scene.control.TableColumn;
import javafx.scene.control.TableRow;
import javafx.scene.control.TableView;
import javafx.scene.layout.BorderPane;
import javafx.stage.Stage;
public class TableViewWithPriorityRowColor extends Application {
#Override
public void start(Stage primaryStage) {
TableView<Job> table = new TableView<>();
table.getColumns().add(column("Name", Job::nameProperty));
table.getColumns().add(column("Value", Job::valueProperty));
table.getColumns().add(column("Priority", Job::priorityProperty));
PseudoClass highPriority = PseudoClass.getPseudoClass("high-priority");
PseudoClass lowPriority = PseudoClass.getPseudoClass("low-priority");
table.setRowFactory(tv -> new TableRow<Job>(){
#Override
public void updateItem(Job job, boolean empty) {
super.updateItem(job, empty);
pseudoClassStateChanged(highPriority, job != null && job.getPriority() == 3);
pseudoClassStateChanged(lowPriority, job != null && job.getPriority() == 1);
}
});
table.getItems().addAll(createJobs());
Scene scene = new Scene(new BorderPane(table), 800, 600);
scene.getStylesheets().add("table-view-with-priority.css");
primaryStage.setScene(scene);
primaryStage.show();
}
public List<Job> createJobs() {
Random rng = new Random();
return IntStream.rangeClosed(1, 40)
.mapToObj(i -> new Job("Job "+i, i, rng.nextInt(3) + 1))
.collect(Collectors.toList());
}
public static <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 Job {
private final StringProperty name = new SimpleStringProperty();
private final IntegerProperty value = new SimpleIntegerProperty();
private final IntegerProperty priority = new SimpleIntegerProperty();
public Job(String name, int value, int priority) {
setName(name);
setValue(value);
setPriority(priority);
}
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 final IntegerProperty valueProperty() {
return this.value;
}
public final int getValue() {
return this.valueProperty().get();
}
public final void setValue(final int value) {
this.valueProperty().set(value);
}
public final IntegerProperty priorityProperty() {
return this.priority;
}
public final int getPriority() {
return this.priorityProperty().get();
}
public final void setPriority(final int priority) {
this.priorityProperty().set(priority);
}
}
public static void main(String[] args) {
launch(args);
}
}
with the CSS code shown above in the file table-view-with-priority.css.

TreeTableView JavaFX 8: I need to make one of columns to use TextFIeld and ChoiceBox when editing

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

Categories