CellEditEvent edit neighbouring cell - javafx

I defined a TableView with 4 columns, the first 3 of them should be editable. The fourth column is the mathematical result of the difference of the second (expenses) and third (earnings) one. I managed this so far but when I edit the second or third column the fourth column dont get updatet. I tried different approaches but it didnt work. The problem is that I dont know how to get access to the neighbouring cell.
package application;
import javafx.application.Application;
import javafx.collections.FXCollections;
import javafx.collections.ObservableList;
import javafx.event.ActionEvent;
import javafx.event.EventHandler;
import javafx.geometry.Insets;
import javafx.stage.Stage;
import javafx.scene.Scene;
import javafx.scene.control.Button;
import javafx.scene.control.TableColumn;
import javafx.scene.control.TableColumn.CellEditEvent;
import javafx.scene.control.TableView;
import javafx.scene.control.TextField;
import javafx.scene.control.cell.PropertyValueFactory;
import javafx.scene.control.cell.TextFieldTableCell;
import javafx.scene.layout.GridPane;
import javafx.scene.layout.HBox;
import javafx.scene.text.Font;
import javafx.scene.text.FontWeight;
import javafx.scene.text.Text;
public class Main extends Application {
private TableView<Member> table = new TableView<>();
//private final ObservableList<Member> data = FXCollections.observableArrayList();
#SuppressWarnings("unchecked")
#Override
public void start(Stage primaryStage) {
try {
//Definition Layout-Container
GridPane primarygridpane = new GridPane();
primarygridpane.setHgap(10);
primarygridpane.setVgap(10);
primarygridpane.setPadding(new Insets(5, 5, 5, 5));
HBox hboxTable = new HBox(10);
hboxTable.setPadding(new Insets(5, 5, 5, 5));
Text titel = new Text("Application");
titel.setFont(Font.font("Arial", FontWeight.BOLD, 28));
// Spalte Name mit Edit-Funktion
TableColumn<Member, String> memberColumn = new TableColumn<>("Name");
memberColumn.setMinWidth(150);
memberColumn.setCellValueFactory(new PropertyValueFactory<Member, String>("member"));
memberColumn.setCellFactory(TextFieldTableCell.forTableColumn());
memberColumn.setOnEditCommit(
new EventHandler<CellEditEvent<Member, String>>() {
#Override
public void handle(CellEditEvent<Member, String> t) {
((Member) t.getTableView().getItems().get(t.getTablePosition().getRow())).setMember(t.getNewValue());
}
}
);
// Spalte Ausgaben mit Edit-Funktion
TableColumn<Member, String> expensesColumn = new TableColumn<>("Ausgaben");
expensesColumn.setMinWidth(50);
expensesColumn.setCellValueFactory(new PropertyValueFactory<>("expenses"));
expensesColumn.setCellFactory(TextFieldTableCell.forTableColumn());
expensesColumn.setOnEditCommit(
new EventHandler<CellEditEvent<Member, String>>() {
#Override
public void handle(CellEditEvent<Member, String> t) {
((Member) t.getTableView().getItems().get(t.getTablePosition().getRow())).setExpenses(t.getNewValue());
//((Member) t.getTableView().getItems().get(t.getTablePosition().getRow())).setDifference(Double.parseDouble(t.getNewValue()));
}
}
);
// Spalte Pfand mit Edit-Funktion
TableColumn<Member, String> earningsColumn = new TableColumn<>("Pfand");
earningsColumn.setMinWidth(50);
earningsColumn.setCellValueFactory(new PropertyValueFactory<Member, String>("earnings"));
earningsColumn.setCellFactory(TextFieldTableCell.forTableColumn());
earningsColumn.setOnEditCommit(
new EventHandler<CellEditEvent<Member, String>>() {
#Override
public void handle(CellEditEvent<Member, String> t) {
((Member) t.getTableView().getItems().get(t.getTablePosition().getRow())).setEarnings(t.getNewValue());
}
}
);
//Spalte Differenz ohne Edit-Funktion
TableColumn<Member, Double> differenceColumn = new TableColumn<>("Differenz");
differenceColumn.setMinWidth(50);
differenceColumn.setCellValueFactory(new PropertyValueFactory<>("difference"));
//Editier-Leiste
TextField tfMember = new TextField();
tfMember.setMinWidth(150);
tfMember.setPromptText("Name");
TextField tfExpenses = new TextField();
tfExpenses.setMinWidth(50);
tfExpenses.setPromptText("Ausgaben");
TextField tfEarnings = new TextField();
tfEarnings.setMinWidth(50);
tfEarnings.setPromptText("Pfand");
Button btnAdd = new Button("Hinzufügen");
Button btnDelete = new Button("Löschen");
hboxTable.getChildren().addAll(tfMember, tfExpenses, tfEarnings, btnAdd, btnDelete);
// Spalten der Tabelle hinzufügen und Tabelle editierbar machen
table.getColumns().addAll(memberColumn, expensesColumn, earningsColumn, differenceColumn);
table.setEditable(true);
// table.setItems(data);
btnAdd.setOnAction(new EventHandler<ActionEvent>() {
#Override
public void handle(ActionEvent e) {
try {
Member member = new Member();
member.setMember(tfMember.getText());
member.setExpenses(tfExpenses.getText());
member.setEarnings(tfEarnings.getText());
member.setDifference(Double.parseDouble(tfExpenses.getText()) - Double.parseDouble(tfEarnings.getText()));
table.getItems().add(member);
//data.add(member);
tfMember.clear();
tfExpenses.clear();
tfEarnings.clear();
} catch (NumberFormatException Exception) {}
}
});
//Elemente dem Gridpane hinzufügen und Rest
primarygridpane.add(titel, 0, 0, 2, 1);
primarygridpane.add(table, 0, 2);
primarygridpane.add(hboxTable, 0, 3);
Scene scene = new Scene(primarygridpane,450,550);
scene.getStylesheets().add(getClass().getResource("application.css").toExternalForm());
primaryStage.setScene(scene);
primaryStage.show();
} catch(Exception e) {e.printStackTrace();}
}
public static void main(String[] args) {
launch(args);
}
}
package application;
import javafx.beans.binding.DoubleBinding;
import javafx.beans.binding.NumberBinding;
import javafx.beans.property.DoubleProperty;
import javafx.beans.property.SimpleDoubleProperty;
import javafx.beans.property.SimpleStringProperty;
import javafx.beans.property.StringProperty;
public class Member {
private SimpleStringProperty member = new SimpleStringProperty();
private DoubleProperty expenses = new SimpleDoubleProperty();
private DoubleProperty earnings = new SimpleDoubleProperty();
private DoubleProperty difference = new SimpleDoubleProperty();
public Member(String member, Double expenses, Double earnings) {
this.member = new SimpleStringProperty(member);
this.expenses = new SimpleDoubleProperty(expenses);
this.earnings = new SimpleDoubleProperty(earnings);
// NumberBinding nb = earningsProperty().subtract(expensesProperty());
// this.difference = new SimpleDoubleProperty(nb);
// NumberBinding nb2 = expenses.subtract(earnings);
}
public String getMember() {
return member.get();
}
public void setMember(String name) {
member.set(name);
}
public final StringProperty MemberProperty(){
return member;
}
public Double getExpenses() {
return expenses.get();
}
public void setExpenses(Double value) {
expenses.set(value);
}
public final DoubleProperty expensesProperty(){
return expenses;
}
public Double getEarnings() {
return earnings.get();
}
public void setEarnings(Double value) {
earnings.set(value);
}
public final DoubleProperty earningsProperty(){
return earnings;
}
public Double getDifference() {
return difference.get();
}
public void setDifference(Double value) {
difference.set(value);
}
public final DoubleProperty differenceProperty(){
return difference;
}
}
I would be very thankfull if someone could help me :)

You can establish the binding simply by calling bind(...) on the property, in the constructor.
Since the differenceProperty() is bound, calling setDifference() would throw an exception, so you should omit that method1. You can do:
public class Member {
private StringProperty member = new SimpleStringProperty();
private DoubleProperty expenses = new SimpleDoubleProperty();
private DoubleProperty earnings = new SimpleDoubleProperty();
private DoubleProperty difference = new SimpleDoubleProperty();
public Member(String member, double expenses, double earnings) {
this.member = new SimpleStringProperty(member);
this.expenses = new SimpleDoubleProperty(expenses);
this.earnings = new SimpleDoubleProperty(earnings);
this.difference = new SimpleDoubleProperty();
this.difference.bind(this.earnings.subtract(this.expenses));
}
public String getMember() {
return member.get();
}
public void setMember(String name) {
member.set(name);
}
public final StringProperty memberProperty(){
return member;
}
public Double getExpenses() {
return expenses.get();
}
public void setExpenses(Double value) {
expenses.set(value);
}
public final DoubleProperty expensesProperty(){
return expenses;
}
public Double getEarnings() {
return earnings.get();
}
public void setEarnings(Double value) {
earnings.set(value);
}
public final DoubleProperty earningsProperty(){
return earnings;
}
public Double getDifference() {
return difference.get();
}
// public void setDifference(Double value) {
// difference.set(value);
// }
public final DoubleProperty differenceProperty(){
return difference;
}
}
In your table, the earnings and expenses columns represent numeric values, so they should be typed appropriately. For reasons explained at JavaFX Properties in TableView, the type here should be Number, not Double. Also note that if you tell your table cell implementation how to convert from a String to the value (Number) you want to display:
expensesColumn.setCellFactory(TextFieldTableCell.forTableColumn(new NumberStringConverter()));
then you no longer need to provide the onEditCommit handler (the table cell will take care of the update).
So your table configuration simplifies to
// Spalte Name mit Edit-Funktion
TableColumn<Member, String> memberColumn = new TableColumn<>("Name");
memberColumn.setMinWidth(150);
memberColumn.setCellValueFactory(new PropertyValueFactory<Member, String>("member"));
memberColumn.setCellFactory(TextFieldTableCell.forTableColumn());
// Spalte Ausgaben mit Edit-Funktion
TableColumn<Member, Number> expensesColumn = new TableColumn<>("Ausgaben");
expensesColumn.setMinWidth(50);
expensesColumn.setCellValueFactory(new PropertyValueFactory<>("expenses"));
expensesColumn.setCellFactory(TextFieldTableCell.forTableColumn(new NumberStringConverter()));
// Spalte Pfand mit Edit-Funktion
TableColumn<Member, Number> earningsColumn = new TableColumn<>("Pfand");
earningsColumn.setMinWidth(50);
earningsColumn.setCellValueFactory(new PropertyValueFactory<>("earnings"));
earningsColumn.setCellFactory(TextFieldTableCell.forTableColumn(new NumberStringConverter()));
//Spalte Differenz ohne Edit-Funktion
TableColumn<Member, Number> differenceColumn = new TableColumn<>("Differenz");
differenceColumn.setMinWidth(50);
differenceColumn.setCellValueFactory(new PropertyValueFactory<>("difference"));
Note that the NumberStringConverter has a fairly simplistic implementation; you might want to implement your own StringConverter<Number> to support, e.g., locale-based string parsing.
Also note that the model class Member now ensures that difference is always the difference between earnings and expenses, so you do not need to (and can not) set the difference property:
btnAdd.setOnAction(new EventHandler<ActionEvent>() {
#Override
public void handle(ActionEvent e) {
try {
Member member = new Member(tfMember.getText(),
Double.parseDouble(tfExpenses.getText()),
Double.parseDouble(tfEarnings.getText()));
table.getItems().add(member);
tfMember.clear();
tfExpenses.clear();
tfEarnings.clear();
} catch (NumberFormatException Exception) {}
}
});
Just as a side note, use of PropertyValueFactory is not really necessary as of Java 8, and because it relies on reflection is prone to failing silently if, e.g., the property name is mistyped (it's also slightly inefficient). You can prefer implementing the callback directly with a lambda expression:
// memberColumn.setCellValueFactory(new PropertyValueFactory<Member, String>("member"));
memberColumn.setCellValueFactory(cellData -> cellData.getValue().memberProperty());
// expensesColumn.setCellValueFactory(new PropertyValueFactory<>("expenses"));
expensesColumn.setCellValueFactory(cellData -> cellData.getValue().expensesProperty());
// earningsColumn.setCellValueFactory(new PropertyValueFactory<>("earnings"));
earningsColumn.setCellValueFactory(cellData -> cellData.getValue().earningsProperty());
This approach also allows you to omit the difference property entirely from the Member class and just use a binding directly in the table column:
differenceColumn.setCellValueFactory(cd ->
cd.getValue().earningsProperty().subtract(cd.getValue().expensesProperty()));
The choice here depends basically on whether you consider the difference an integral part of the data (so it should be included in the model), or whether the data just consist of the earnings and expenses, and the difference is just something you want to visualize in the table.
(1) Really, you should use a ReadOnlyProperty here to represent the difference, since calling differenceProperty().set(...) would also throw an exception with the code the way it's written. Basically:
private final ReadOnlyDoubleWrapper difference = new ReadOnlyDoubleWrapper() ;
// Constructor and other properties as before ...
public final ReadOnlyDoubleProperty differenceProperty() {
return difference.getReadOnlyProperty();
}
public final double getDifference() {
return differenceProperty().get();
}

Related

JavaFX Observable List only one property with value true

I would like to ask you, there is the simple way to add constraint to Observable list that are only one value true of property.
For example:
I Have an observable list of Persons. The class person has boolean Property "isKing" and string property "guild". It can be only one king for each guild.
Sure, I can ( and I know how) add an listener to change value of this property for others persons if new king will be added. But there is other way ??
( there is no database in local aplication to manage data integrations...)
It doesn't seem like such a property should be part of the elements of the list, but instead should be just an external value. In the example you suggest, you could create a separate class that encapsulated your list and the king:
public class Guild {
private final ReadOnlyObjectWrapper<Person> king = new ReadOnlyObjectWrapper<>();
private final ObservableList<Person> members = FXCollections.observableArrayList();
public ObservableList<Person> getMembers() {
return members ;
}
public ReadOnlyObjectProperty<Person> kingProperty() {
return king.getReadOnlyProperty();
}
public Person getKing() {
return kingProperty().get();
}
public void setKing(Person king) {
if (! population.contains(king)) {
throw new IllegalArgumentException("king must be a member of the guild");
}
this.king.set(king);
}
}
If the situation is more complex (e.g. a single population and multiple guilds, each with a king) the same basic idea should be feasible: define a model class that encapsulates whatever data structures you need, for example an ObservableList<Person> population and an ObservableMap<String, Person> kings, the latter keeping a single king for each guild.
Here is a more complex example:
Person.java:
package model;
import javafx.beans.property.SimpleStringProperty;
import javafx.beans.property.StringProperty;
public class Person {
private final StringProperty name = new SimpleStringProperty();
private final StringProperty guild = new SimpleStringProperty();
public Person(String name, String guild) {
setName(name);
setGuild(guild);
}
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 StringProperty guildProperty() {
return this.guild;
}
public final String getGuild() {
return this.guildProperty().get();
}
public final void setGuild(final String guild) {
this.guildProperty().set(guild);
}
}
DataModel.java:
package model;
import javafx.beans.value.ChangeListener;
import javafx.collections.FXCollections;
import javafx.collections.MapChangeListener.Change;
import javafx.collections.ObservableList;
import javafx.collections.ObservableMap;
public class DataModel {
private final ObservableMap<String, Person> kings = FXCollections.observableHashMap() ;
private final ObservableList<Person> population = FXCollections.observableArrayList() ;
public DataModel() {
// if a current king switches guild, remove them as king:
ChangeListener<String> guildListener = (obs, oldGuild, newGuild) ->
kings.remove(oldGuild);
kings.addListener((Change<? extends String, ? extends Person> c) -> {
if (c.wasAdded()) {
c.getValueAdded().guildProperty().addListener(guildListener);
}
if (c.wasRemoved()) {
c.getValueRemoved().guildProperty().removeListener(guildListener);
}
});
}
public ObservableList<Person> getPopulation() {
return population ;
}
public ObservableMap<String, Person> getKings() {
return FXCollections.unmodifiableObservableMap(kings);
}
public void makeKing(Person person) {
kings.put(person.getGuild(), person);
}
}
ConstrainedModelDemo.java:
package ui;
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.binding.Bindings;
import javafx.beans.property.SimpleStringProperty;
import javafx.beans.value.ObservableValue;
import javafx.scene.Scene;
import javafx.scene.control.CheckBox;
import javafx.scene.control.TableCell;
import javafx.scene.control.TableColumn;
import javafx.scene.control.TableView;
import javafx.scene.control.cell.ComboBoxTableCell;
import javafx.scene.layout.BorderPane;
import javafx.stage.Stage;
import model.DataModel;
import model.Person;
public class ConstrainedModelDemo extends Application {
#Override
public void start(Stage primaryStage) {
DataModel model = new DataModel();
List<String> guilds = IntStream.rangeClosed(1, 5).mapToObj(i -> "Guild "+i).collect(Collectors.toList());
Random rng = new Random();
List<Person> population = IntStream.rangeClosed(1, 100).mapToObj(i -> new Person("Person "+i, guilds.get(rng.nextInt(guilds.size())))).collect(Collectors.toList());
model.getPopulation().setAll(population);
TableView<Person> table = new TableView<>();
table.setEditable(true);
table.getItems().addAll(population);
table.getColumns().add(column("Name", Person::nameProperty));
TableColumn<Person, String> guildColumn = column("Guild", Person::guildProperty);
guildColumn.setCellFactory(ComboBoxTableCell.forTableColumn(guilds.toArray(new String[guilds.size()])));
table.getColumns().add(guildColumn);
TableColumn<Person, Boolean> kingCol = new TableColumn<>("King");
kingCol.setCellValueFactory(cellData -> {
Person p = cellData.getValue();
return Bindings.createBooleanBinding(() ->
p == model.getKings().get(p.getGuild()), p.guildProperty(), model.getKings());
});
kingCol.setCellFactory(tc -> new TableCell<Person, Boolean>() {
private final CheckBox checkBox = new CheckBox();
{
checkBox.setOnAction(e -> {
if (checkBox.isSelected()) {
model.makeKing(table.getItems().get(getIndex()));
} else {
model.getKings().remove(table.getItems().get(getIndex()).getGuild());
}
});
}
#Override
protected void updateItem(Boolean item, boolean empty) {
super.updateItem(item, empty);
if (empty) {
setGraphic(null);
} else {
checkBox.setSelected(item);
setGraphic(checkBox);
}
}
});
table.getColumns().add(kingCol);
TableView<String> kingTable = new TableView<>();
kingTable.getItems().addAll(guilds);
kingTable.getColumns().add(column("Guild", guild -> new SimpleStringProperty(guild)));
kingTable.getColumns().add(column("King", guild -> Bindings.createStringBinding(() -> {
if (guild == null) return null ;
Person king = model.getKings().get(guild);
if (king == null) return null ;
return king.getName() ;
}, model.getKings())));
BorderPane root = new BorderPane(table);
root.setRight(kingTable);
Scene scene = new Scene(root, 600, 600);
primaryStage.setScene(scene);
primaryStage.show();
}
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 void main(String[] args) {
launch(args);
}
}

JavaFX TableView: Show sorted items + Background Task

I'm working with Javafx and I'm struggling to get a sorted table in place when using background Tasks to update. In the code here, which can be run standalone, I update a table in background.
What I'd like to do is that this table gets updated, and stays sorted in chronological order, so older train times appear at the top, and later ones at the bottom. The example produces times that are the opposite order on purpose, to see if sorting works.
I run some tests before I added concurrent update to the table, and the way I would do it is by calling:
private final ObservableList<StationBoardLine> data = FXCollections.observableArrayList(
new StationBoardLine("RE", "17:14", "Basel Bad Bf", "Basel SBB", "+3", "RE 5343"));
SortedList<StationBoardLine> sorted = new SortedList<>(data, new DelayComparator());
table.setItems(sorted);
However, now I'm not setting the items, but using the background task together with ReadOnlyObjectProperty and ReadOnlyObjectWrapper to append to it.
So, my question is, how can I make sure that as items are added, the list remains ordered? I've tried to see if I could reorder the list inside the call to Platform.runLater but didn't seem to work.
The link between the task updating the table and the table is set is here:
table.itemsProperty().bind(task.partialResultsProperty());
Thanks for help,
Galder
The way I would do it is by updating an ObservableList by the background task and use it as a "source" to create a SortedList. This SortedList would then act as the source of "items" to the TableView.
A general structure would be :
public class MyClass {
private TableView<T> tableView = new TableView;
private ObservableList<T> sourceList = FXCollections.observableArrayList();
public MyClass() {
...
SortedList<T> sortedList = new SortedList<>(sourceList, new MyComparator());
tableView.setItems(sortedList);
...
new Task<Void> {
protected Void call() {
... // Some background data fetch
Platform.runLater(() -> sourceList.add(data));
return null;
}
};
}
}
For your scenario, I would go with something that you already have. Therefore, instead of creating a new ObservableList to be used as a source for your SortedList, I would use the list returned by Task#getPartialResults().
The DelayComparator uses the value of the delay to compare and show the data in the TableView.
import javafx.application.Application;
import javafx.application.Platform;
import javafx.beans.property.SimpleStringProperty;
import javafx.collections.FXCollections;
import javafx.collections.ObservableList;
import javafx.collections.transformation.SortedList;
import javafx.concurrent.Task;
import javafx.scene.Scene;
import javafx.scene.control.TableColumn;
import javafx.scene.control.TableView;
import javafx.scene.control.cell.PropertyValueFactory;
import javafx.scene.layout.BorderPane;
import javafx.stage.Stage;
import java.util.Comparator;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
public class App extends Application {
private TableView<StationBoardLine> table = new TableView<>();
private final ExecutorService exec = Executors.newSingleThreadExecutor();
public static void main(String[] args) {
launch(args);
}
#Override
public void start(Stage stage) {
BorderPane root = new BorderPane();
Scene scene = new Scene(root, 800, 600);
table.setColumnResizePolicy(TableView.CONSTRAINED_RESIZE_POLICY);
table.setEditable(true);
TableColumn typeCol = getTableCol("Type", 10, "type");
TableColumn departureCol = getTableCol("Departure", 30, "departure");
TableColumn stationCol = getTableCol("Station", 200, "station");
TableColumn destinationCol = getTableCol("Destination", 200, "destination");
TableColumn delayCol = getTableCol("Delay", 20, "delay");
TableColumn trainName = getTableCol("Train Name", 50, "trainName");
table.getColumns().addAll(
typeCol, departureCol, stationCol, destinationCol, delayCol, trainName);
root.setCenter(table);
PartialResultsTask task = new PartialResultsTask();
SortedList<StationBoardLine> sorted = new SortedList<>(task.getPartialResults(), new DelayComparator());
table.setItems(sorted);
exec.submit(task);
stage.setTitle("Swiss Transport Delays Board");
stage.setScene(scene);
stage.show();
}
private TableColumn getTableCol(String colName, int minWidth, String fieldName) {
TableColumn<StationBoardLine, String> typeCol = new TableColumn<>(colName);
typeCol.setMinWidth(minWidth);
typeCol.setCellValueFactory(new PropertyValueFactory<>(fieldName));
return typeCol;
}
static final class DelayComparator implements Comparator<StationBoardLine> {
#Override
public int compare(StationBoardLine o1, StationBoardLine o2) {
return o1.getDelay().compareTo(o2.getDelay());
}
}
public class PartialResultsTask extends Task<Void> {
private ObservableList<StationBoardLine>partialResults = FXCollections.observableArrayList();
public final ObservableList<StationBoardLine> getPartialResults() {
return partialResults;
}
#Override protected Void call() throws Exception {
System.out.println("Creating station board entries...");
for (int i=5; i >= 1; i--) {
Thread.sleep(1000);
if (isCancelled()) break;
StationBoardLine l = new StationBoardLine(
"ICE", "16:" + i, "Basel Bad Bf", "Chur", String.valueOf(i), "ICE 75");
Platform.runLater(() -> partialResults.add(l));
}
return null;
}
}
public static final class StationBoardLine {
private final SimpleStringProperty type;
private final SimpleStringProperty departure;
private final SimpleStringProperty station;
private final SimpleStringProperty destination;
private final SimpleStringProperty delay;
private final SimpleStringProperty trainName;
StationBoardLine(String type,
String departure,
String station,
String destination,
String delay,
String trainName) {
this.type = new SimpleStringProperty(type);
this.departure = new SimpleStringProperty(departure);
this.station = new SimpleStringProperty(station);
this.destination = new SimpleStringProperty(destination);
this.delay = new SimpleStringProperty(delay);
this.trainName = new SimpleStringProperty(trainName);
}
public String getType() {
return type.get();
}
public SimpleStringProperty typeProperty() {
return type;
}
public void setType(String type) {
this.type.set(type);
}
public String getDeparture() {
return departure.get();
}
public SimpleStringProperty departureProperty() {
return departure;
}
public void setDeparture(String departure) {
this.departure.set(departure);
}
public String getStation() {
return station.get();
}
public SimpleStringProperty stationProperty() {
return station;
}
public void setStation(String station) {
this.station.set(station);
}
public String getDestination() {
return destination.get();
}
public SimpleStringProperty destinationProperty() {
return destination;
}
public void setDestination(String destination) {
this.destination.set(destination);
}
public String getDelay() {
return delay.get();
}
public SimpleStringProperty delayProperty() {
return delay;
}
public void setDelay(String delay) {
this.delay.set(delay);
}
public String getTrainName() {
return trainName.get();
}
public SimpleStringProperty trainNameProperty() {
return trainName;
}
public void setTrainName(String trainName) {
this.trainName.set(trainName);
}
}
}

javafx : get the name of the column

How do I get the name of the column of a textfield inside a javaFX table?
I need this to check the value of the cells only in the "text2" column. I tried it with textfield.parent() but I didn't get a useful result.Edit: I just removed some unnessary log, which was not helpful for understanding.Now it is more convenient.
Here is my Code:
import java.util.ArrayList;
import javafx.beans.property.SimpleStringProperty;
import javafx.beans.property.StringProperty;
import javafx.beans.value.ChangeListener;
import javafx.beans.value.ObservableValue;
import javafx.scene.control.ContentDisplay;
import javafx.scene.control.TableCell;
import javafx.scene.control.TableColumn;
import javafx.scene.control.TextArea;
import javafx.util.Callback;
import javafx.application.Application;
import static javafx.application.Application.launch;
import javafx.beans.property.IntegerProperty;
import javafx.beans.property.SimpleIntegerProperty;
import javafx.collections.FXCollections;
import javafx.collections.ObservableList;
import javafx.event.ActionEvent;
import javafx.event.EventHandler;
import javafx.geometry.Insets;
import javafx.scene.Scene;
import javafx.scene.control.Button;
import javafx.scene.control.Label;
import javafx.scene.control.TableView;
import javafx.scene.control.cell.PropertyValueFactory;
import javafx.scene.layout.BorderPane;
import javafx.scene.layout.HBox;
import javafx.stage.Stage;
/*interface inside_table
{
public String get_column_name
}*/
public class Supermain extends Application {
#Override
public void start(Stage primaryStage) {
ArrayList myindizes=new ArrayList();
final TableView<myTextRow> table = new TableView<>();
table.setEditable(true);
table.setStyle("-fx-text-wrap: true;");
//Table columns
TableColumn<myTextRow, String> clmID = new TableColumn<>("ID");
clmID.setMinWidth(160);
clmID.setCellValueFactory(new PropertyValueFactory<>("ID"));
TableColumn<myTextRow, String> clmtext = new TableColumn<>("Text");
clmtext.setMinWidth(160);
clmtext.setCellValueFactory(new PropertyValueFactory<>("text"));
clmtext.setCellFactory(new TextFieldCellFactory());
TableColumn<myTextRow, String> clmtext2 = new TableColumn<>("Text2");
clmtext2.setMinWidth(160);
clmtext2.setCellValueFactory(new PropertyValueFactory<>("text2"));
clmtext2.setCellFactory(new TextFieldCellFactory());
//Add data
final ObservableList<myTextRow> data = FXCollections.observableArrayList(
new myTextRow(5, "Lorem","bla"),
new myTextRow(2, "Ipsum","bla")
);
table.getColumns().addAll(clmID, clmtext,clmtext2);
table.setItems(data);
HBox hBox = new HBox();
hBox.setSpacing(5.0);
hBox.setPadding(new Insets(5, 5, 5, 5));
Button btn = new Button();
btn.setText("Get Data");
btn.setOnAction(new EventHandler<ActionEvent>() {
#Override
public void handle(ActionEvent event) {
for (myTextRow data1 : data) {
System.out.println("data:" + data1.getText2());
}
}
});
hBox.getChildren().add(btn);
BorderPane pane = new BorderPane();
pane.setTop(hBox);
pane.setCenter(table);
primaryStage.setScene(new Scene(pane, 640, 480));
primaryStage.show();
}
/**
* #param args the command line arguments
*/
public static void main(String[] args) {
launch(args);
}
public static class TextFieldCellFactory
implements Callback<TableColumn<myTextRow, String>, TableCell<myTextRow, String>> {
#Override
public TableCell<myTextRow, String> call(TableColumn<myTextRow, String> param) {
TextFieldCell textFieldCell = new TextFieldCell();
return textFieldCell;
}
public static class TextFieldCell extends TableCell<myTextRow, String> {
private TextArea textField;
private StringProperty boundToCurrently = null;
private String last_text;
public TextFieldCell() {
textField = new TextArea();
textField.setWrapText(true);
textField.setMinWidth(this.getWidth() - this.getGraphicTextGap() * 2);
last_text="";
this.setGraphic(textField);
textField.focusedProperty().addListener((obs, wasFocused, isNowFocused) -> {
//only if textfield is in the text2 column
if(isNowFocused){last_text=textField.getText(); System.out.println("NOW focus "+last_text);}
if (! isNowFocused && ! isValid(textField.getText())) {
textField.setText(last_text);
textField.selectAll();
System.out.println("blur");
}
});
}
#Override
protected void updateItem(String item, boolean empty) {
super.updateItem(item, empty);
if (!empty) {
// Show the Text Field
this.setContentDisplay(ContentDisplay.GRAPHIC_ONLY);
// myindizes.add(getIndex());
// Retrieve the actual String Property that should be bound to the TextField
// If the TextField is currently bound to a different StringProperty
// Unbind the old property and rebind to the new one
ObservableValue<String> ov = getTableColumn().getCellObservableValue(getIndex());
SimpleStringProperty sp = (SimpleStringProperty) ov;
if (this.boundToCurrently == null) {
this.boundToCurrently = sp;
this.textField.textProperty().bindBidirectional(sp);
} else if (this.boundToCurrently != sp) {
this.textField.textProperty().unbindBidirectional(this.boundToCurrently);
this.boundToCurrently = sp;
this.textField.textProperty().bindBidirectional(this.boundToCurrently);
}
double height = real_lines_height(textField.getText(), this.getWidth(), 30, 22);
textField.setPrefHeight(height);
textField.setMaxHeight(height);
textField.setMaxHeight(Double.MAX_VALUE);
// if height bigger than the biggest height in the row
//-> change all heights of the row(textfields ()typeof textarea) to this height
// else leave the height as it is
//System.out.println("item=" + item + " ObservableValue<String>=" + ov.getValue());
//this.textField.setText(item); // No longer need this!!!
} else {
this.setContentDisplay(ContentDisplay.TEXT_ONLY);
}
}//update
private boolean isValid(String s){
if(s.length()<7){return true;}
return false;
}
}
}
public class myTextRow {
private final SimpleIntegerProperty ID;
private final SimpleStringProperty text;
private final SimpleStringProperty text2;
public myTextRow(int ID, String text,String text2) {
this.ID = new SimpleIntegerProperty(ID);
this.text = new SimpleStringProperty(text);
this.text2 = new SimpleStringProperty(text2);
}
//setter
public void setID(int id) {
this.ID.set(id);
}
public void setText(String text) {
this.text.set(text);
}
public void setText2(String text) {
this.text2.set(text);
}
//getter
public int getID() {
return ID.get();
}
public String getText() {
return text.get();
}
public String getText2() {
return text2.get();
}
//properties
public StringProperty textProperty() {
return text;
}
public StringProperty text2Property() {
return text2;
}
public IntegerProperty IDProperty() {
return ID;
}
}
private static double real_lines_height(String s, double width, double heightCorrector, double widthCorrector) {
HBox h = new HBox();
Label l = new Label("Text");
h.getChildren().add(l);
Scene sc = new Scene(h);
l.applyCss();
double line_height = l.prefHeight(-1);
int new_lines = s.replaceAll("[^\r\n|\r|\n]", "").length();
// System.out.println("new lines= "+new_lines);
String[] lines = s.split("\r\n|\r|\n");
// System.out.println("line count func= "+ lines.length);
int count = 0;
//double rest=0;
for (int i = 0; i < lines.length; i++) {
double text_width = get_text_width(lines[i]);
double plus_lines = Math.ceil(text_width / (width - widthCorrector));
if (plus_lines > 1) {
count += plus_lines;
//rest+= (text_width / (width-widthCorrector)) - plus_lines;
} else {
count += 1;
}
}
//count+=(int) Math.ceil(rest);
count += new_lines - lines.length;
return count * line_height + heightCorrector;
}
private static double get_text_width(String s) {
HBox h = new HBox();
Label l = new Label(s);
l.setWrapText(false);
h.getChildren().add(l);
Scene sc = new Scene(h);
l.applyCss();
return l.prefWidth(-1);
}
}
There are probably (way) better ways to organize this, but probably the cleanest fix is just to define a boolean validate parameter to the constructor of your cell implementation. (You really don't want the logic to be "if the title of the column is equal to some specific text, then validate". You would be utterly screwed when your boss came in to the office and asked you to internationalize the application, or even just change the title of the column, for example.)
Using an entire inner class just to implement the callback seems completely redundant, but keeping that you would have to pass the parameter through it:
public static class TextFieldCellFactory
implements Callback<TableColumn<myTextRow, String>, TableCell<myTextRow, String>> {
private final boolean validate ;
public TextFieldCellFactory(boolean validate) {
this.validate = validate ;
}
#Override
public TableCell<myTextRow, String> call(TableColumn<myTextRow, String> param) {
TextFieldCell textFieldCell = new TextFieldCell(validate);
return textFieldCell;
}
public static class TextFieldCell extends TableCell<myTextRow, String> {
private TextArea textField;
private StringProperty boundToCurrently = null;
private String last_text;
public TextFieldCell(boolean validate) {
textField = new TextArea();
textField.setWrapText(true);
textField.setMinWidth(this.getWidth() - this.getGraphicTextGap() * 2);
last_text="";
this.setGraphic(textField);
if (validate) {
textField.focusedProperty().addListener((obs, wasFocused, isNowFocused) -> {
//only if textfield is in the text2 column
if(isNowFocused){last_text=textField.getText(); System.out.println("NOW focus "+last_text);}
if (! isNowFocused && ! isValid(textField.getText())) {
textField.setText(last_text);
textField.selectAll();
System.out.println("blur");
}
});
}
}
// ...
}
Then of course you just do
TableColumn<myTextRow, String> clmtext = new TableColumn<>("Text");
clmtext.setMinWidth(160);
clmtext.setCellValueFactory(new PropertyValueFactory<>("text"));
clmtext.setCellFactory(new TextFieldCellFactory(false));
TableColumn<myTextRow, String> clmtext2 = new TableColumn<>("Text2");
clmtext2.setMinWidth(160);
clmtext2.setCellValueFactory(new PropertyValueFactory<>("text2"));
clmtext2.setCellFactory(new TextFieldCellFactory(true));
(To properly answer your question, you can get the text of the column from within the cell to which it is attached with getTableColumn().getText(), but as I pointed out, actually basing the logic on the value displayed in a column header will make your code completely unmaintainable.)
And I guess for completeness, I should also mention that your TextFieldCellFactory class looks like it is not really serving any purpose. I would remove it entirely and just have the TextFieldCell class, and do
TableColumn<myTextRow, String> clmtext = new TableColumn<>("Text");
clmtext.setMinWidth(160);
clmtext.setCellValueFactory(new PropertyValueFactory<>("text"));
clmtext.setCellFactory(c -> new TextFieldCell(false));
TableColumn<myTextRow, String> clmtext2 = new TableColumn<>("Text2");
clmtext2.setMinWidth(160);
clmtext2.setCellValueFactory(new PropertyValueFactory<>("text2"));
clmtext2.setCellFactory(c -> new TextFieldCell(true));

JavaFX Tableview - column value dependent on other columns

I have a TableView in JavaFX. It has a field subTotal which depends on the value of the fields quantity and price. I added a new column for the subTotal.
I have textfields present to add a new row to the table. But, the add button wants to have another textfield for the subTotal, although it does not really necessary for the subtotal column.
What I have tried so far :
TableColumn columnCodeProduct = new TableColumn("Product Code");
columnCodeProduct.setMinWidth(100);
columnCodeProduct.setCellValueFactory(new PropertyValueFactory<Data , Integer>("productname "));
TableColumn columnProductName = new TableColumn("Product Name");
columnProductName.setMinWidth(140);
columnProductName.setCellValueFactory(new PropertyValueFactory<Data , String>("codeproduct"));
TableColumn columnPrice = new TableColumn("Price");
columnPrice.setMinWidth(100);
columnPrice.setCellValueFactory(new PropertyValueFactory<Data , Integer>("price"));
TableColumn columQuantity = new TableColumn("Quantity");
columQuantity.setMinWidth(100);
columQuantity.setCellValueFactory(new PropertyValueFactory<Data , Integer>("quantity"));
TableColumn columnTotal = new TableColumn("Sub Total");
columnTotal.setMinWidth(100);
columQuantity.setCellValueFactory(new PropertyValueFactory<Data , Integer>("sub"));
tableData.getColumns().addAll(columnCodeProduct , columnProductName , columnPrice , columQuantity );
tableData.setItems(data);
addButton = new Button("Add Item");
addButton.setOnAction(new EventHandler<ActionEvent>() {
#Override
public void handle(ActionEvent event)
{
if(addproCodeTextfield.getText().isEmpty() || addproNameTextfield.getText().isEmpty()
|| addPriceTextfield.getText().isEmpty() || quantityTextField.getText().isEmpty())
{
System.out.println("Please Add information to all the fields");
} else {
data.add(new Data (
addproCodeTextfield.getText(),
addproNameTextfield.getText(),
addPriceTextfield.getText(),
quantityTextField.getText()));
methodTotal();
}
}
});
Data Class
public class Data
{
private final SimpleStringProperty codeproduct;
private final SimpleStringProperty productname;
private final SimpleStringProperty price ;
private final SimpleStringProperty quantity;
public Data (String code , String proname , String presyo , String quant )
{
this.codeproduct = new SimpleStringProperty(code);
this.productname = new SimpleStringProperty(proname);
this.price = new SimpleStringProperty(presyo);
this.quantity = new SimpleStringProperty(quant);
}
public String getcodeProduct()
{
return codeproduct.get();
}
public String getproductName()
{
return productname.get();
}
public String getPrice()
{
return price.get();
}
public String getQuantity()
{
return quantity.get();
}
}
I would restructure your model class as #ItachiUchiha suggests. If you feel you need to keep the data stored with String representations, you can just create a binding for the subtotal column:
TableColumn<Data, Number> subtotalColumn = new TableColumn<>("Sub Total");
subTotalColumn.setCellValueFactory(cellData -> {
Data data = cellData.getValue();
return Bindings.createDoubleBinding(
() -> {
try {
double price = Double.parseDouble(data.getPrice());
int quantity = Integer.parseInt(data.getQuantity());
return price * quantity ;
} catch (NumberFormatException nfe) {
return 0 ;
}
},
data.priceProperty(),
data.quantityProperty()
);
});
You can take benefit from JavaFX's power to bind value.
Few points to take care of while implementing a scenario as stated above:
The POJO class(in your case Data) fields must have correct types. For example price and quantity must be of SimpleIntegerProperty instead of SimpleStringProperty. This will help us in using Bindings.
SubTotal field depends on the values of price and quantity. The best way to achieve this is to bind subTotalProperty to a multiply Binding of price and quantity.
I have created a (not so) simple example basic editable tableview to show the approach. It has additional features, like editable cells, that you (or others seeking the same problem) might need ;)
import javafx.application.Application;
import javafx.beans.binding.Bindings;
import javafx.beans.binding.NumberBinding;
import javafx.beans.property.SimpleIntegerProperty;
import javafx.beans.property.SimpleStringProperty;
import javafx.collections.FXCollections;
import javafx.collections.ObservableList;
import javafx.event.ActionEvent;
import javafx.event.EventHandler;
import javafx.geometry.Insets;
import javafx.scene.Group;
import javafx.scene.Scene;
import javafx.scene.control.*;
import javafx.scene.control.TableColumn.CellEditEvent;
import javafx.scene.control.cell.PropertyValueFactory;
import javafx.scene.control.cell.TextFieldTableCell;
import javafx.scene.layout.HBox;
import javafx.scene.layout.VBox;
import javafx.scene.text.Font;
import javafx.stage.Stage;
import javafx.util.converter.NumberStringConverter;
public class TableViewSample extends Application {
private TableView<Product> table = new TableView<Product>();
private final ObservableList<Product> data =
FXCollections.observableArrayList(
new Product("Notebook", 10, 12),
new Product("Eraser", 20, 12),
new Product("Pencil", 30, 12),
new Product("Pen", 40, 12),
new Product("Glue", 50, 12));
final HBox hb = new HBox();
public static void main(String[] args) {
launch(args);
}
#Override
public void start(Stage stage) {
Scene scene = new Scene(new Group());
stage.setTitle("Book Store Sample");
stage.setWidth(650);
stage.setHeight(550);
final Label label = new Label("Book Store");
label.setFont(new Font("Arial", 20));
table.setEditable(true);
TableColumn name = new TableColumn("Name");
name.setMinWidth(100);
name.setCellValueFactory(
new PropertyValueFactory<Product, String>("name"));
name.setCellFactory(TextFieldTableCell.forTableColumn());
name.setOnEditCommit(
new EventHandler<CellEditEvent<Product, String>>() {
#Override
public void handle(CellEditEvent<Product, String> t) {
((Product) t.getTableView().getItems().get(
t.getTablePosition().getRow())
).setName(t.getNewValue());
}
}
);
TableColumn priceCol = new TableColumn("Price");
priceCol.setMinWidth(100);
priceCol.setCellValueFactory(
new PropertyValueFactory<Product, String>("price"));
priceCol.setCellFactory(TextFieldTableCell.<Product, Number>forTableColumn(new NumberStringConverter()));
priceCol.setOnEditCommit(
new EventHandler<CellEditEvent<Product, Number>>() {
#Override
public void handle(CellEditEvent<Product, Number> t) {
((Product) t.getTableView().getItems().get(
t.getTablePosition().getRow())
).setPrice(t.getNewValue().intValue());
}
}
);
TableColumn quantityCol = new TableColumn("Quantity");
quantityCol.setMinWidth(200);
quantityCol.setCellValueFactory(
new PropertyValueFactory<Product, Number>("quantity"));
quantityCol.setCellFactory(TextFieldTableCell.<Product, Number>forTableColumn(new NumberStringConverter()));
quantityCol.setOnEditCommit(
new EventHandler<CellEditEvent<Product, Number>>() {
#Override
public void handle(CellEditEvent<Product, Number> t) {
((Product) t.getTableView().getItems().get(
t.getTablePosition().getRow())
).setQuantity(t.getNewValue().intValue());
}
}
);
TableColumn subTotalCol = new TableColumn("Sub Total");
subTotalCol.setMinWidth(200);
subTotalCol.setCellValueFactory(
new PropertyValueFactory<Product, String>("subTotal"));
table.setItems(data);
table.getColumns().addAll(name, priceCol, quantityCol, subTotalCol);
final TextField addName = new TextField();
addName.setPromptText("Name");
addName.setMaxWidth(name.getPrefWidth());
final TextField addPrice = new TextField();
addPrice.setMaxWidth(priceCol.getPrefWidth());
addPrice.setPromptText("Price");
final TextField addQuantity = new TextField();
addQuantity.setMaxWidth(quantityCol.getPrefWidth());
addQuantity.setPromptText("Quantity");
final Button addButton = new Button("Add");
addButton.setOnAction(new EventHandler<ActionEvent>() {
#Override
public void handle(ActionEvent e) {
data.add(new Product(
name.getText(),
Integer.parseInt(addPrice.getText()),
Integer.parseInt(addQuantity.getText())));
addName.clear();
addPrice.clear();
addQuantity.clear();
}
});
hb.getChildren().addAll(addName, addPrice, addQuantity, addButton);
hb.setSpacing(3);
final VBox vbox = new VBox();
vbox.setSpacing(5);
vbox.setPadding(new Insets(10, 0, 0, 10));
vbox.getChildren().addAll(label, table, hb);
((Group) scene.getRoot()).getChildren().addAll(vbox);
stage.setScene(scene);
stage.show();
}
public static class Product {
private final SimpleStringProperty name;
private final SimpleIntegerProperty price;
private final SimpleIntegerProperty quantity;
private final SimpleIntegerProperty subTotal;
private Product(String name, int price, int quantity) {
this.name = new SimpleStringProperty(name);
this.price = new SimpleIntegerProperty(price);
this.quantity = new SimpleIntegerProperty(quantity);
this.subTotal = new SimpleIntegerProperty();
NumberBinding multiplication = Bindings.multiply(this.priceProperty(), this.quantityProperty());
this.subTotalProperty().bind(multiplication);
}
public String getName() {
return name.get();
}
public SimpleStringProperty nameProperty() {
return name;
}
public void setName(String name) {
this.name.set(name);
}
public int getPrice() {
return price.get();
}
public SimpleIntegerProperty priceProperty() {
return price;
}
public void setPrice(int price) {
this.price.set(price);
}
public int getQuantity() {
return quantity.get();
}
public SimpleIntegerProperty quantityProperty() {
return quantity;
}
public void setQuantity(int quantity) {
this.quantity.set(quantity);
}
public int getSubTotal() {
return subTotal.get();
}
public SimpleIntegerProperty subTotalProperty() {
return subTotal;
}
public void setSubTotal(int subTotal) {
this.subTotal.set(subTotal);
}
}
}
Screenshot
Note - I have defined setCellFactory and setOnCommit to each of the columns. This is because the name, price and quantity columns are editable. You are most welcome to remove them in case you do not seek editable property.

How to fetch value of selected item in choice box from a table view coloumn in javafx

How can i fetch the value of the selected choice from the choce box in the following table.
column3 has 13 choice box nodes populated using following code.I want to fetch the selected item.
final ObservableList LogLevelList=FXCollections.observableArrayList("FATAL", "ERROR", "WARN", "INFO", "INOUT", "DEBUG");
column3.setCellFactory(new Callback<TableColumn<Feature,String>,TableCell<Feature,String>>(){
#Override
public TableCell<Feature,String> call(TableColumn<Feature,String> param) {
TableCell<Feature,String> cell = new TableCell<Feature,String>(){
#Override
public void updateItem(String item, boolean empty) {
System.out.println("Inside UpdateItem");
ChoiceBox choice = new ChoiceBox(LogLevelList);
choice.getSelectionModel().select(LogLevelList.indexOf(item));
//SETTING ALL THE GRAPHICS COMPONENT FOR CELL
setGraphic(choice);
}
};
return cell;
}
});
Does the predefined ChoiceBoxTableCell do what you need?
column3.setCellFactory(ChoiceBoxTableCell.forTableColumn(logLevelList));
See if this helps:
import java.util.stream.Collectors;
import java.util.stream.IntStream;
import javafx.application.Application;
import javafx.beans.property.SimpleStringProperty;
import javafx.beans.property.StringProperty;
import javafx.collections.FXCollections;
import javafx.collections.ObservableList;
import javafx.scene.Scene;
import javafx.scene.control.Button;
import javafx.scene.control.TableColumn;
import javafx.scene.control.TableView;
import javafx.scene.control.cell.ChoiceBoxTableCell;
import javafx.scene.control.cell.PropertyValueFactory;
import javafx.scene.layout.BorderPane;
import javafx.stage.Stage;
public class TableChoiceBoxTest extends Application {
#Override
public void start(Stage primaryStage) {
final TableView<Feature> table = new TableView<>();
table.setEditable(true);
final TableColumn<Feature, String> nameCol = new TableColumn<>("Name");
nameCol.setCellValueFactory(new PropertyValueFactory<>("name"));
final TableColumn<Feature, String> logLevelCol = new TableColumn<>("Log level");
logLevelCol.setCellValueFactory(new PropertyValueFactory<>("logLevel"));
logLevelCol.setPrefWidth(150);
final ObservableList<String> logLevelList = FXCollections.observableArrayList("FATAL", "ERROR", "WARN", "INFO", "INOUT", "DEBUG");
logLevelCol.setCellFactory(ChoiceBoxTableCell.forTableColumn(logLevelList));
table.getColumns().addAll(nameCol, logLevelCol);
table.getItems().setAll(
IntStream.rangeClosed(1, 20)
.mapToObj(i -> new Feature("Item "+i, "FATAL"))
.collect(Collectors.toList())
);
Button showDataButton = new Button("Dump data");
showDataButton.setOnAction(event -> table.getItems().forEach(System.out::println));
BorderPane root = new BorderPane();
root.setCenter(table);
root.setBottom(showDataButton);
Scene scene = new Scene(root, 400, 600);
primaryStage.setScene(scene);
primaryStage.show();
}
public static class Feature {
private final StringProperty name ;
private final StringProperty logLevel ;
public Feature(String name, String logLevel) {
this.name = new SimpleStringProperty(this, "name", name);
this.logLevel = new SimpleStringProperty(this, "logLevel", logLevel);
}
public StringProperty nameProperty() {
return name ;
}
public final String getName() {
return name.get();
}
public final void setName(String name) {
this.name.set(name);
}
public StringProperty logLevelProperty() {
return logLevel ;
}
public final String getLogLevel() {
return logLevel.get();
}
public final void setLogLevel(String logLevel) {
this.logLevel.set(logLevel);
}
#Override
public String toString() {
return getName() + ": " + getLogLevel();
}
}
public static void main(String[] args) {
launch(args);
}
}
The provided ChoiceBoxTableCell updates the property of the associated item for you, so there's never any need to get the value from the ChoiceBox; you can just get the value from your model object.
I think there are mistakes in your code. You do not want to display your Choice box in each and every cell of that column (i.e Emptied Row's Cell) and Also you should call super class function.
Now for getting the selected value of ChoiceBox , instead of just displaying your choicebox with the values you will have to save them in some ArrayList or Map or best options is to save inside your Feature class. So that you can finally use
#Override
public void updateItem(String item, boolean empty) {
super.updateItem(item,empty);
if(item != null){
ChoiceBox choice = new ChoiceBox(LogLevelList);
choice.getSelectionModel().select(LogLevelList.indexOf(item));
choice.getSelectionModel().selectedItemProperty().addListener(new ChangeListener<String>() {
#Override
public void changed(ObservableValue<? extends String> ov, String t, String t1) {
//either use : myMap.put(getIndex(),t1);
//or : item.setChoice(t1);
}
});
//SETTING ALL THE GRAPHICS COMPONENT FOR CELL
setGraphic(choice);
}
}
Also for demo of ChoiceBox in TableView there is one blog post for you :http://blog.ngopal.com.np/2011/10/01/tableview-cell-modifiy-in-javafx/

Resources