I have a TabPane where users enter/edit data on each tab and can freely switch between tabs without having to save changes before switching to a new tab. One tab has a TableView, and I'd like to prevent users from leaving that tab if they enter invalid data. My original approach was along the same lines as this question, which does not quite work - the tab is not reliably changed back. I liked James_D's answer and tried to implement something similar. However, most of the time the data being entered into a table is optional, so disabling other tabs until a user enters data is not an option.
What I ultimately did was extend TableColumn to add a BooleanProperty 'invalid' which I then bind to Tab's disableProperty. In that column's commit event, I validate the new value and, if it doesn't pass, set invalid = true, which disables the appropriate tab. This also does not quite work. I have custom table cells that commit edits on loss of focus. If focus is lost to clicking a different tab, the commit event is too late - the tab is first selected, then disabled. I've been wracking my brain for a workaround, but am out of ideas. If anyone has any suggestions, I would really appreciate it!
Short example (clear out any last name and click Tab 2):
import javafx.application.Application;
import javafx.application.Platform;
import javafx.beans.property.BooleanProperty;
import javafx.beans.property.SimpleBooleanProperty;
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.Alert;
import javafx.scene.control.Alert.AlertType;
import javafx.scene.control.ButtonType;
import javafx.scene.control.Tab;
import javafx.scene.control.TabPane;
import javafx.scene.control.TableCell;
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.stage.Stage;
import javafx.util.Callback;
public class TabPaneTableTest extends Application {
#Override
public void start(Stage primaryStage) {
TableView<Person> table = new TableView<>();
ObservableList<Person> data = FXCollections.observableArrayList();
table.setEditable(true);
MyTableColumn<Person, String> firstNameCol = new MyTableColumn<>("First Name");
firstNameCol.setCellValueFactory(new PropertyValueFactory<Person, String>("firstName"));
MyTableColumn<Person, String> lastNameCol = new MyTableColumn<>("Last Name");
lastNameCol.setCellValueFactory(new PropertyValueFactory<Person, String>("lastName"));
Callback<TableColumn<Person, String>, TableCell<Person, String>> cellFactory = (TableColumn<Person, String> p) -> new MyEditingCell<Person>();
firstNameCol.setCellFactory(cellFactory);
lastNameCol.setCellFactory(cellFactory);
firstNameCol.setOnEditCommit((CellEditEvent<Person, String> event) -> {
event.getRowValue().setFirstName(event.getNewValue());
});
lastNameCol.setOnEditCommit((CellEditEvent<Person, String> event) -> {
if(event.getNewValue().trim().isEmpty()) {
new Alert(AlertType.ERROR, "Last name must be filled out!", ButtonType.OK).showAndWait();
lastNameCol.setInvalid(true);
}
else {
event.getRowValue().setLastName(event.getNewValue());
lastNameCol.setInvalid(false);
}
});
table.getColumns().addAll(firstNameCol, lastNameCol);
table.setItems(data);
data.add(new Person("Luke", "Skywalker"));
data.add(new Person("Han", "Solo"));
data.add(new Person("R2", "D2"));
TabPane tabPane = new TabPane();
Tab tab1 = new Tab("Tab 1");
tab1.setClosable(false);
tab1.setContent(table);
Tab tab2 = new Tab("Tab 2");
tab2.setClosable(false);
tab2.disableProperty().bind(lastNameCol.invalidProperty());
tabPane.getTabs().addAll(tab1, tab2);
Scene scene = new Scene(tabPane, 400, 200);
primaryStage.setTitle("Tab Pane Table Validation Test");
primaryStage.setScene(scene);
primaryStage.show();
}
public class MyEditingCell<S> extends TableCell<S, String> {
private TextField editingField;
private void createEditingField() {
editingField = new TextField(getString());
editingField.focusedProperty().addListener((ov, oldValue, newValue) -> {
if(!newValue) {
commitEdit(editingField.getText());
}
});
}
#Override
public void startEdit() {
super.startEdit();
createEditingField();
setText(null);
setGraphic(editingField);
Platform.runLater(() -> {
editingField.requestFocus();
editingField.selectAll();
});
}
#Override
public void cancelEdit() {
super.cancelEdit();
setText((String)getItem());
setGraphic(null);
}
#Override
public void updateItem(String item, boolean empty) {
super.updateItem(item, empty);
if(empty) {
setText(null);
setGraphic(null);
}
else {
if(isEditing()) {
if(editingField != null) {
editingField.setText(getString());
}
setText(null);
setGraphic(editingField);
}
else {
setText(getString());
setGraphic(null);
}
}
}
private String getString() {
return getItem() == null ? "" : getItem().toString();
}
}
public class MyTableColumn<S, T> extends TableColumn<S, T> {
private BooleanProperty invalid = new SimpleBooleanProperty(false);
public MyTableColumn(String header) {
super(header);
setEditable(true);
}
public BooleanProperty invalidProperty() {
return invalid;
}
public boolean getInvalid() {
return invalid.get();
}
public void setInvalid(boolean value) {
invalid.set(value);
}
}
public class Person {
private StringProperty firstName;
private StringProperty lastName;
public Person(String first, String last) {
firstName = new SimpleStringProperty(this, "firstName", first);
lastName = new SimpleStringProperty(this, "lastName", last);
}
public void setFirstName(String value) {
firstNameProperty().set(value);
}
public String getFirstName() {
return firstNameProperty().get();
}
public StringProperty firstNameProperty() {
if(firstName == null)
firstName = new SimpleStringProperty(this, "firstName", "First");
return firstName;
}
public void setLastName(String value) {
lastNameProperty().set(value);
}
public String getLastName() {
return lastNameProperty().get();
}
public StringProperty lastNameProperty() {
if(lastName == null)
lastName = new SimpleStringProperty(this, "lastName", "Last");
return lastName;
}
}
public static void main(String[] args) {
launch(args);
}
}
If you create your observable list with an extractor, for example:
ObservableList<Person> data = FXCollections.observableArrayList(person ->
new Observable[] { person.lastNameProperty() });
then the list will fire update notifications any time any of the specified properties change in any of the elements (in this case, any time the lastName changes on anything in the list).
So now you can create a binding for invalid:
BooleanBinding invalid = Bindings.createBooleanBinding(
() -> data.stream().anyMatch(person -> person.getLastName().isEmpty()),
data);
And then you can just observe that binding:
invalid.addListener((obs, wasInvalid, isNowInvalid) -> {
if (isNowInvalid) {
// show alert, etc...
}
});
or disable a node by binding to it:
someNode.disableProperty().bind(invalid);
You could similarly bind this invalid property in your TableColumn subclass (if you still need that) to this binding.
Related
In my Spring Boot JavaFX application I have multiple TableViews. The user is allowed to reorder the columns by using the default drag-and-drop functionality. I also have a listener to detect that another row in one of those TableViews is selected and take some action accordingly:
/*
* Processing when a selection in a table changes.
*/
getTableView().getSelectionModel().selectedItemProperty().addListener((observable, oldValue, newValue) -> {
this.detailsController.get().showDetails(newValue);
});
Problem is that this listener gets activated when a column is dragged and then dropped (on the drop part of the action). This has undesired side effects, since the variable newValue is 'null' in that case (which in itself is a valid value for processing, I just don't want to pass that value when dropping a column after dragging). Is there a way to bypass this listener when the column is dropped?
I have tried various ways to catch the drag-drop events, but to no avail...I was thinking I could deactivate the listener when the drag starts and reactivate after the drop is done.
Here is some sample code:
import java.util.Arrays;
import java.util.List;
import java.util.function.Function;
import javafx.application.Application;
import javafx.beans.property.SimpleStringProperty;
import javafx.beans.property.StringProperty;
import javafx.beans.value.ObservableValue;
import javafx.scene.Scene;
import javafx.scene.control.TableColumn;
import javafx.scene.control.TableView;
import javafx.scene.layout.BorderPane;
import javafx.scene.layout.VBox;
import javafx.stage.Stage;
public class TestDragDrop extends Application {
#Override
public void start(Stage primaryStage) {
TableView<Person> table = new TableView<>();
table.getColumns().add(column("First Name", Person::firstNameProperty));
table.getColumns().add(column("Last Name", Person::lastNameProperty));
table.getColumns().add(column("Email", Person::emailProperty));
table.getItems().addAll(createData());
table.getSelectionModel().selectedItemProperty().addListener((observable, oldValue, newValue) -> {
if (newValue == null) {
System.out.println("===>>> Oops");
} else {
System.out.println("===>>> Hi there " + newValue.getFirstName());
}
});
VBox checkBoxes = new VBox(5);
checkBoxes.getStyleClass().add("controls");
BorderPane root = new BorderPane(table);
root.setTop(checkBoxes);
Scene scene = new Scene(root, 800, 600);
primaryStage.setScene(scene);
primaryStage.show();
}
private static <S, T> TableColumn<S, T> column(String text, Function<S, ObservableValue<T>> property) {
TableColumn<S, T> col = new TableColumn<>(text);
col.setCellValueFactory(cellData -> property.apply(cellData.getValue()));
return col;
}
private List<Person> createData() {
return Arrays.asList(new Person("Jacob", "Smith", "jacob.smith#example.com"),
new Person("Isabella", "Johnson", "isabella.johnson#example.com"),
new Person("Ethan", "Williams", "ethan.williams#example.com"),
new Person("Emma", "Jones", "emma.jones#example.com"),
new Person("Michael", "Brown", "michael.brown#example.com"));
}
public static class Person {
private final StringProperty firstName = new SimpleStringProperty();
private final StringProperty lastName = new SimpleStringProperty();
private final StringProperty email = new SimpleStringProperty();
public Person(String firstName, String lastName, String email) {
setFirstName(firstName);
setLastName(lastName);
setEmail(email);
}
public final StringProperty firstNameProperty() {
return this.firstName;
}
public final String getFirstName() {
return this.firstNameProperty().get();
}
public final void setFirstName(final String firstName) {
this.firstNameProperty().set(firstName);
}
public final StringProperty lastNameProperty() {
return this.lastName;
}
public final String getLastName() {
return this.lastNameProperty().get();
}
public final void setLastName(final String lastName) {
this.lastNameProperty().set(lastName);
}
public final StringProperty emailProperty() {
return this.email;
}
public final String getEmail() {
return this.emailProperty().get();
}
public final void setEmail(final String email) {
this.emailProperty().set(email);
}
}
public static void main(String[] args) {
launch(args);
}
}
Select a row in the table: ===>>> Hi there .... is output to the console. Now drag the first column to a different place in the table: ===>>> Oops is output to the console.
So one way to prevent this is by adding a buffer to prevent changes for a period of time once the column has been released.
In my case I used 50ms as the buffer because it will be hard for a person to finish dragging and click on a name in that time as it comes out to .05 Seconds in my testing this worked fine(No null were passed) but increase/decrease as you see fit
Here I initialize the PauseTransition which will fire after a given time
private final PauseTransition bufferReset = new PauseTransition(Duration.millis(50));
private boolean isBuffering = false;
Once initialized set the variable to flip back to no longer buffering
bufferReset.setOnFinished(event -> isBuffering = false);
The next block of code is where we flip the buffer variable after the column has been released and start the timer to flip the variable back
Platform.runLater(() -> {
for (Node header : table.lookupAll("TableHeaderRow")) {
if(header instanceof TableHeaderRow) {
header.addEventFilter(MouseEvent.MOUSE_RELEASED, event -> {
isBuffering = true;
bufferReset.play();
});
}
}
});
From there wrap your code in a isBuffering if statement
if(!isBuffering) {
if (newValue == null) {
System.out.println("===>>> Oops");
} else {
System.out.println("===>>> Hi there " + newValue.getFirstName());
}
}
Full Code(Not including the person class):
public class TestDragDrop extends Application {
private final PauseTransition bufferReset = new PauseTransition(Duration.millis(50));
private boolean isBuffering = false;
#Override
public void start(Stage primaryStage) {
TableView<Person> table = new TableView<>();
table.getColumns().add(column("First Name", Person::firstNameProperty));
table.getColumns().add(column("Last Name", Person::lastNameProperty));
table.getColumns().add(column("Email", Person::emailProperty));
table.getItems().addAll(createData());
table.getSelectionModel().selectedItemProperty().addListener((observable, oldValue, newValue) -> {
if(!isBuffering) {
if (newValue == null) {
System.out.println("===>>> Oops");
} else {
System.out.println("===>>> Hi there " + newValue.getFirstName());
}
}
});
bufferReset.setOnFinished(event -> isBuffering = false);
Platform.runLater(() -> {
for (Node header : table.lookupAll("TableHeaderRow")) {
if(header instanceof TableHeaderRow) {
header.addEventFilter(MouseEvent.MOUSE_RELEASED, event -> {
isBuffering = true;
bufferReset.play();
});
}
}
});
VBox checkBoxes = new VBox(5);
checkBoxes.getStyleClass().add("controls");
BorderPane root = new BorderPane(table);
root.setTop(checkBoxes);
Scene scene = new Scene(root, 800, 600);
primaryStage.setScene(scene);
primaryStage.show();
}
private static <S, T> TableColumn<S, T> column(String text, Function<S, ObservableValue<T>> property) {
TableColumn<S, T> col = new TableColumn<>(text);
col.setCellValueFactory(cellData -> property.apply(cellData.getValue()));
return col;
}
private List<Person> createData() {
return Arrays.asList(new Person("Jacob", "Smith", "jacob.smith#example.com"),
new Person("Isabella", "Johnson", "isabella.johnson#example.com"),
new Person("Ethan", "Williams", "ethan.williams#example.com"),
new Person("Emma", "Jones", "emma.jones#example.com"),
new Person("Michael", "Brown", "michael.brown#example.com"));
}
public static void main(String[] args) { launch(args); }
}
I am trying to create a ComboBox that displays multiple columns in its dropdown menu.
Here is a screenshot that shows how I want it to look:
Any suggestions?
The only solution that is in my head is to create a custom container by extending ComboBox and customizing it with multiple columns.
But does JavaFX even provide me the option to create a custom UI container?
How do you create a custom UI container and how to use it in FXML?
You do not need to extend ComboBox to create a similar layout. Instead, you just need to provide your own implementation of a CellFactory.
By creating a custom CellFactory, you can control how the items in your ComboBox are displayed by providing your own Listcell (the item that is actually selectable in the dropdown menu).
I am certain there are many ways to accomplish this, but for this example, I'm going to use a GridPane as the root layout for my ListCell.
The complete example below has comments throughout as well:
import javafx.application.Application;
import javafx.beans.property.SimpleStringProperty;
import javafx.beans.property.StringProperty;
import javafx.collections.FXCollections;
import javafx.collections.ObservableList;
import javafx.geometry.Insets;
import javafx.geometry.Pos;
import javafx.scene.Scene;
import javafx.scene.control.ComboBox;
import javafx.scene.control.Label;
import javafx.scene.control.ListCell;
import javafx.scene.layout.ColumnConstraints;
import javafx.scene.layout.GridPane;
import javafx.scene.layout.VBox;
import javafx.stage.Stage;
public class Main extends Application {
public static void main(String[] args) {
launch(args);
}
#Override
public void start(Stage primaryStage) {
// Simple Interface
VBox root = new VBox(10);
root.setAlignment(Pos.CENTER);
root.setPadding(new Insets(10));
// List of sample Persons
ObservableList<Person> persons = FXCollections.observableArrayList();
persons.addAll(
new Person("Maria Anders", "Sales Representative", "Zurich"),
new Person("Ana Trujillo", "Owner", "Sydney"),
new Person("Thomas Hardy", "Order Administrator", "Dallas")
);
// Create a simple ComboBox of Persons
ComboBox<Person> cboPersons = new ComboBox<>();
cboPersons.setItems(persons);
// We need a StringConverter in order to ensure the selected item is displayed properly
// For this sample, we only want the Person's name to be displayed
cboPersons.setConverter(new StringConverter<Person>() {
#Override
public String toString(Person person) {
return person.getName();
}
#Override
public Person fromString(String string) {
return null;
}
});
// Provide our own CellFactory to control how items are displayed
cboPersons.setCellFactory(cell -> new ListCell<Person>() {
// Create our layout here to be reused for each ListCell
GridPane gridPane = new GridPane();
Label lblName = new Label();
Label lblTitle = new Label();
Label lblLocation = new Label();
// Static block to configure our layout
{
// Ensure all our column widths are constant
gridPane.getColumnConstraints().addAll(
new ColumnConstraints(100, 100, 100),
new ColumnConstraints(100, 100, 100),
new ColumnConstraints(100, 100, 100)
);
gridPane.add(lblName, 0, 1);
gridPane.add(lblTitle, 1, 1);
gridPane.add(lblLocation, 2, 1);
}
// We override the updateItem() method in order to provide our own layout for this Cell's graphicProperty
#Override
protected void updateItem(Person person, boolean empty) {
super.updateItem(person, empty);
if (!empty && person != null) {
// Update our Labels
lblName.setText(person.getName());
lblTitle.setText(person.getTitle());
lblLocation.setText(person.getLocation());
// Set this ListCell's graphicProperty to display our GridPane
setGraphic(gridPane);
} else {
// Nothing to display here
setGraphic(null);
}
}
});
// Add the ComboBox to the scene
root.getChildren().addAll(
new Label("Select Person:"),
cboPersons
);
// Show the stage
primaryStage.setScene(new Scene(root));
primaryStage.setTitle("Sample");
primaryStage.show();
}
}
// Simple Person class to represent our...Persons
class Person {
private final StringProperty name = new SimpleStringProperty();
private final StringProperty title = new SimpleStringProperty();
private final StringProperty location = new SimpleStringProperty();
Person(String name, String title, String location) {
this.name.set(name);
this.title.set(title);
this.location.set(location);
}
public String getName() {
return name.get();
}
public void setName(String name) {
this.name.set(name);
}
public StringProperty nameProperty() {
return name;
}
public String getTitle() {
return title.get();
}
public void setTitle(String title) {
this.title.set(title);
}
public StringProperty titleProperty() {
return title;
}
public String getLocation() {
return location.get();
}
public void setLocation(String location) {
this.location.set(location);
}
public StringProperty locationProperty() {
return location;
}
}
The Results:
I am trying to make a tableview clickable, where it will return the text in the cell that is clicked. I am receiving two errors when trying to compile in Netbeans. All of the code was taken from "Example 12-11: Alternative Solution Of Cell Editing" official tableview tutorial & from this stackoverflow.com answer. Here are the errors:
type argument MouseEvent is not within bounds of type-variable T
cell.addEventFilter(MouseEvent.MOUSE_CLICKED, new EventHandler() {
where T is a type-variable:
T extends Event declared in interface EventHandler
method addEventFilter in class Node cannot be applied to given types;
cell.addEventFilter(MouseEvent.MOUSE_CLICKED, new EventHandler() {
required: EventType,EventHandler
found: int,>
reason: no instance(s) of type variable(s) T exist so that argument type int conforms to formal parameter type EventType
where T is a type-variable:
T extends Event declared in method addEventFilter(EventType,EventHandler)
import java.awt.event.MouseEvent;
import javafx.application.Application;
import javafx.beans.property.SimpleStringProperty;
import javafx.beans.value.ChangeListener;
import javafx.beans.value.ObservableValue;
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.Button;
import javafx.scene.control.Label;
import javafx.scene.control.TableCell;
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.layout.HBox;
import javafx.scene.layout.VBox;
import javafx.scene.text.Font;
import javafx.stage.Stage;
import javafx.util.Callback;
public class TableViewSample extends Application {
private TableView<Person> table = new TableView<>();
private final ObservableList<Person> data =
FXCollections.observableArrayList(
new Person("Jacob", "Smith", "jacob.smith#example.com"),
new Person("Isabella", "Johnson", "isabella.johnson#example.com"),
new Person("Ethan", "Williams", "ethan.williams#example.com"),
new Person("Emma", "Jones", "emma.jones#example.com"),
new Person("Michael", "Brown", "michael.brown#example.com"));
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("Table View Sample");
stage.setWidth(450);
stage.setHeight(550);
final Label label = new Label("Address Book");
label.setFont(new Font("Arial", 20));
table.setEditable(false);
Callback<TableColumn, TableCell> cellFactory =
new Callback<TableColumn, TableCell>() {
public TableCell call(TableColumn p) {
TableCell cell = new TableCell<Person, String>() {
#Override
public void updateItem(String item, boolean empty) {
super.updateItem(item, empty);
setText(empty ? null : getString());
setGraphic(null);
}
private String getString() {
return getItem() == null ? "" : getItem().toString();
}
};
cell.addEventFilter(MouseEvent.MOUSE_CLICKED, new EventHandler<MouseEvent>() {
#Override
public void handle(MouseEvent event) {
if (event.getClickCount() > 1) {
System.out.println("double clicked!");
TableCell c = (TableCell) event.getSource();
System.out.println("Cell text: " + c.getText());
}
}
});
return cell;
}
};
TableColumn firstNameCol = new TableColumn("First Name");
firstNameCol.setMinWidth(100);
firstNameCol.setCellValueFactory(
new PropertyValueFactory<Person, String>("firstName"));
firstNameCol.setCellFactory(cellFactory);
firstNameCol.setOnEditCommit(
new EventHandler<CellEditEvent<Person, String>>() {
#Override
public void handle(CellEditEvent<Person, String> t) {
((Person) t.getTableView().getItems().get(
t.getTablePosition().getRow())
).setFirstName(t.getNewValue());
}
}
);
TableColumn lastNameCol = new TableColumn("Last Name");
lastNameCol.setMinWidth(100);
lastNameCol.setCellValueFactory(
new PropertyValueFactory<Person, String>("lastName"));
lastNameCol.setCellFactory(cellFactory);
lastNameCol.setOnEditCommit(
new EventHandler<CellEditEvent<Person, String>>() {
#Override
public void handle(CellEditEvent<Person, String> t) {
((Person) t.getTableView().getItems().get(
t.getTablePosition().getRow())
).setLastName(t.getNewValue());
}
}
);
TableColumn emailCol = new TableColumn("Email");
emailCol.setMinWidth(200);
emailCol.setCellValueFactory(
new PropertyValueFactory<Person, String>("email"));
emailCol.setCellFactory(cellFactory);
emailCol.setOnEditCommit(
new EventHandler<CellEditEvent<Person, String>>() {
#Override
public void handle(CellEditEvent<Person, String> t) {
((Person) t.getTableView().getItems().get(
t.getTablePosition().getRow())
).setEmail(t.getNewValue());
}
}
);
table.setItems(data);
table.getColumns().addAll(firstNameCol, lastNameCol, emailCol);
final TextField addFirstName = new TextField();
addFirstName.setPromptText("First Name");
addFirstName.setMaxWidth(firstNameCol.getPrefWidth());
final TextField addLastName = new TextField();
addLastName.setMaxWidth(lastNameCol.getPrefWidth());
addLastName.setPromptText("Last Name");
final TextField addEmail = new TextField();
addEmail.setMaxWidth(emailCol.getPrefWidth());
addEmail.setPromptText("Email");
final Button addButton = new Button("Add");
addButton.setOnAction(new EventHandler<ActionEvent>() {
#Override
public void handle(ActionEvent e) {
data.add(new Person(
addFirstName.getText(),
addLastName.getText(),
addEmail.getText()));
addFirstName.clear();
addLastName.clear();
addEmail.clear();
}
});
hb.getChildren().addAll(addFirstName, addLastName, addEmail, 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 Person {
private final SimpleStringProperty firstName;
private final SimpleStringProperty lastName;
private final SimpleStringProperty email;
private Person(String fName, String lName, String email) {
this.firstName = new SimpleStringProperty(fName);
this.lastName = new SimpleStringProperty(lName);
this.email = new SimpleStringProperty(email);
}
public String getFirstName() {
return firstName.get();
}
public void setFirstName(String fName) {
firstName.set(fName);
}
public String getLastName() {
return lastName.get();
}
public void setLastName(String fName) {
lastName.set(fName);
}
public String getEmail() {
return email.get();
}
public void setEmail(String fName) {
email.set(fName);
}
}
class EditingCell extends TableCell<Person, String> {
private TextField textField;
public EditingCell() {
}
#Override
public void startEdit() {
if (!isEmpty()) {
super.startEdit();
createTextField();
setText(null);
setGraphic(textField);
textField.selectAll();
}
}
#Override
public void cancelEdit() {
super.cancelEdit();
setText((String) getItem());
setGraphic(null);
}
#Override
public void updateItem(String item, boolean empty) {
super.updateItem(item, empty);
if (empty) {
setText(null);
setGraphic(null);
} else {
if (isEditing()) {
if (textField != null) {
textField.setText(getString());
}
setText(null);
setGraphic(textField);
} else {
setText(getString());
setGraphic(null);
}
}
}
private void createTextField() {
textField = new TextField(getString());
textField.setMinWidth(this.getWidth() - this.getGraphicTextGap()* 2);
textField.focusedProperty().addListener(new ChangeListener<Boolean>(){
#Override
public void changed(ObservableValue<? extends Boolean> arg0,
Boolean arg1, Boolean arg2) {
if (!arg2) {
commitEdit(textField.getText());
}
}
});
}
private String getString() {
return getItem() == null ? "" : getItem();
}
}
}
You have the wrong import for MouseEvent. You need javafx.scene.input.MouseEvent, not the AWT version.
It is likely that when your IDE prompted you to import MouseEvent, the first option was java.awt.event.MouseEvent, rather than javafx.scene.input.MouseEvent.
Be careful while importing with JavaFX, as many of it's classes have the same names as classes in standard Java libraries.
I'm using javafx to do some table stuff. I want to validate my textfields in the myTextRow Class. In the "setText2" method I check the input if it is not bigger than 6 symbols, but it has no effects at all.
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;
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;
public TextFieldCell() {
textField = new TextArea();
textField.setWrapText(true);
textField.setMinWidth(this.getWidth() - this.getGraphicTextGap() * 2);
this.setGraphic(textField);
}
#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);
}
}
}
}
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);
}
public void setID(int id) {
this.ID.set(id);
}
public void setText(String text) {
this.text.set(text);
}
public void setText2(String text) {
if(text2check(text)){
this.text2.set(text);}
else
{System.out.println("wrong value!!!");}
}
public int getID() {
return ID.get();
}
public String getText() {
return text.get();
}
public StringProperty textProperty() {
return text;
}
public String getText2() {
return text2.get();
}
public StringProperty text2Property() {
return text2;
}
public IntegerProperty IDProperty() {
return ID;
}
public boolean text2check(String t)
{
if(t.length()>6)return false;
return true;
}
}
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();
// System.out.println("dubbyloop.FXMLDocumentController.get_text_width(): "+l.prefWidth(-1));
return l.prefWidth(-1);
}
}
A rule of the JavaFX Properties pattern is that for a property x, invoking xProperty().setValue(value) should always be identical to invoking setX(value). Your validation makes this not true. The binding your cell implementation uses invokes the setValue method on the property, which is why it bypasses your validation check.
(Side note: in all the code I am going to change the names so that they adhere to proper naming conventions.)
The default way to implement a property in this pattern is:
public class MyTextRow {
private final StringProperty text = new SimpleStringProperty();
public StringProperty textProperty() {
return text ;
}
public final void setText(String text) {
textProperty().set(text);
}
public final String getText() {
return textProperty().get();
}
}
By having the set/get methods delegate to the appropriate property methods, you are guaranteed these rules are enforced, even if the textProperty() methods is overridden in a subclass. Making the set and get methods final ensures that the rule is not broken by a subclass overriding those methods.
One approach might be to override the set and setValue methods in the property, as follows:
public class MyTextRow {
private final StringProperty text2 = new StringPropertyBase() {
#Override
public String getName() {
return "text2";
}
#Override
public Object getBean() {
return MyTextRow.this ;
}
#Override
public void setValue(String value) {
if (text2Check(value)) {
super.setValue(value);
}
}
#Override
public void set(String value) {
if (text2Check(value)) {
super.set(value);
}
}
}
public StringProperty text2Property() {
return text2 ;
}
public final void setText2(String text2) {
text2Property().set(text2);
}
public final String getText2() {
return text2Property().get();
}
// ...
}
however, I think this will break the bidirectional binding that you have with the text property in the TextArea (basically, there is no way to communicate back to the text area when a change is vetoed, so the text area will not know to revert to the previous value). One fix would be to implement your cell using listeners on the properties instead of bindings. You could use a TextFormatter on the text area that simply updates the property and vetoes the text change if the change doesn't occur.
Here is a complete SSCCE using this approach:
import java.util.function.Function;
import java.util.function.UnaryOperator;
import javafx.application.Application;
import javafx.beans.property.Property;
import javafx.beans.property.SimpleStringProperty;
import javafx.beans.property.StringProperty;
import javafx.beans.property.StringPropertyBase;
import javafx.scene.Scene;
import javafx.scene.control.ContentDisplay;
import javafx.scene.control.TableCell;
import javafx.scene.control.TableColumn;
import javafx.scene.control.TableView;
import javafx.scene.control.TextArea;
import javafx.scene.control.TextFormatter;
import javafx.scene.control.TextFormatter.Change;
import javafx.stage.Stage;
public class VetoStringChange extends Application {
#Override
public void start(Stage primaryStage) {
TableView<Item> table = new TableView<>();
table.setEditable(true);
table.getColumns().add(column("Item", Item::nameProperty));
table.getColumns().add(column("Description", Item::descriptionProperty));
for (int i = 1; i <= 20 ; i++) {
table.getItems().add(new Item("Item "+i, ""));
}
primaryStage.setScene(new Scene(table, 600, 600));
primaryStage.show();
}
public static <S> TableColumn<S,String> column(String title, Function<S,Property<String>> property) {
TableColumn<S,String> col = new TableColumn<>(title);
col.setCellValueFactory(cellData -> property.apply(cellData.getValue()));
col.setCellFactory(tc -> new TextAreaCell<S>(property));
col.setPrefWidth(200);
return col ;
}
public static class TextAreaCell<S> extends TableCell<S, String> {
private TextArea textArea ;
public TextAreaCell(Function<S, Property<String>> propertyAccessor) {
textArea = new TextArea();
textArea.setWrapText(true);
textArea.setMinWidth(this.getWidth() - this.getGraphicTextGap() * 2);
textArea.setMaxHeight(Double.MAX_VALUE);
UnaryOperator<Change> filter = c -> {
String proposedText = c.getControlNewText() ;
Property<String> prop = propertyAccessor.apply(getTableView().getItems().get(getIndex()));
prop.setValue(proposedText);
if (prop.getValue().equals(proposedText)) {
return c ;
} else {
return null ;
}
};
textArea.setTextFormatter(new TextFormatter<String>(filter));
this.setGraphic(textArea);
}
#Override
protected void updateItem(String item, boolean empty) {
super.updateItem(item, empty);
if (!empty) {
if (! textArea.getText().equals(item)) {
textArea.setText(item);
}
// Show the Text Field
this.setContentDisplay(ContentDisplay.GRAPHIC_ONLY);
} else {
this.setContentDisplay(ContentDisplay.TEXT_ONLY);
}
}
}
public static class Item {
private final StringProperty name = new StringPropertyBase() {
#Override
public Object getBean() {
return Item.this;
}
#Override
public String getName() {
return "name" ;
}
#Override
public void set(String value) {
if (checkValue(value)) {
super.set(value);
}
}
#Override
public void setValue(String value) {
if (checkValue(value)) {
super.setValue(value);
}
}
};
private final StringProperty description = new SimpleStringProperty();
public Item(String name, String description) {
setName(name);
setDescription(description);
}
private boolean checkValue(String value) {
return value.length() <= 6 ;
}
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 descriptionProperty() {
return this.description;
}
public final String getDescription() {
return this.descriptionProperty().get();
}
public final void setDescription(final String description) {
this.descriptionProperty().set(description);
}
}
public static void main(String[] args) {
launch(args);
}
}
Another approach is to allow a "commit and revert" type strategy on your property:
public class MyTextRow {
private final StringProperty text2 = new SimpleStringProperty();
public MyTextRow() {
text2.addListener((obs, oldText, newText) -> {
if (! checkText2(newText)) {
// sanity check:
if (checkText2(oldText)) {
text2.set(oldText);
}
}
});
}
public StringProperty text2Property() {
return text ;
}
public final void setText2(String text2) {
text2Property().set(text2);
}
public final String getText2() {
return text2Property().get();
}
}
In general I dislike validation by listening for an invalid value and reverting like this, because other listeners to the property will see all the changes, including changes to and from invalid values. However, this might be the best option in this case.
Finally, you could consider vetoing invalid changes as in the first option, and also setting a TextFormatter on the control in the cell that simply doesn't allow text entry that results in an invalid string. This isn't always possible from a usability perspective (e.g. if empty strings are invalid, you almost always want to allow the user to temporarily delete all the text), and it means keeping two validation checks in sync in your code, which is a pain.
I am working with JavaFx 2.2. I am having a problem that I am not able to place different components in a TableView Column. For example I have two columns
1) Answer
2) AnswerType
If AnswerType contains “Multiple Choice” then the corresponding cell in Answer Column should display a ComboBox else it should display a TextField.
I have a code example below but its displaying either ComboBox or TextField but not both in different cells of same column.
import javafx.application.Application;
import javafx.beans.property.SimpleStringProperty;
import javafx.collections.FXCollections;
import javafx.collections.ObservableList;
import javafx.geometry.Insets;
import javafx.scene.Scene;
import javafx.scene.control.TableCell;
import javafx.scene.control.TableColumn;
import javafx.scene.control.TableView;
import javafx.scene.control.cell.PropertyValueFactory;
import javafx.scene.control.cell.TextFieldTableCell;
import javafx.scene.layout.GridPane;
import javafx.scene.layout.StackPane;
import javafx.stage.Stage;
import javafx.util.Callback;
import javafx.scene.control.cell.ComboBoxTableCell;
public class TableCellWithMultipleComponent extends Application {
#SuppressWarnings("rawtypes")
TableColumn answerTypeCol;
#SuppressWarnings("rawtypes")
TableColumn answerCol;
ObservableList<String> namesChoiceList;
public static void main(String[] args) {
launch(args);
}
#SuppressWarnings({ "rawtypes", "unchecked" })
#Override
public void start(final Stage primaryStage) {
primaryStage.setTitle("Table Cell With Multiple Components");
TableView<Person> table = new TableView<Person>();
table.setEditable(true);
final ObservableList<Person> data =
FXCollections.observableArrayList(
new Person("A", "Multiple Choice"),
new Person("JOHN", "Free Text"),
new Person("123", "Free Text"),
new Person("D", "Multiple Choice")
);
GridPane gridpane = new GridPane();
gridpane.setPadding(new Insets(5));
gridpane.setHgap(5);
gridpane.setVgap(5);
namesChoiceList = FXCollections.observableArrayList("A", "B", "C", "D", "INVALID_ANSWER", "NO_ANSWER");
answerCol = new TableColumn();
answerCol.setText("Answers");
answerCol.setMinWidth(210);
answerCol.setEditable(true);
answerCol.setCellValueFactory(new PropertyValueFactory("answers"));
answerCol.setCellFactory( new Callback<TableColumn<String, String>, TableCell<String, String>>() {
#Override
public TableCell<String, String> call(TableColumn<String, String> arg0) {
return new anyMethod();
}
});
answerTypeCol = new TableColumn();
answerTypeCol.setText("Answers Type");
answerTypeCol.setMinWidth(210);
answerTypeCol.setEditable(true);
answerTypeCol.setCellValueFactory(new PropertyValueFactory("answersType"));
table.setItems(data);
table.getColumns().addAll(answerCol, answerTypeCol);
StackPane root = new StackPane();
Scene scene =new Scene(root, 500, 550);
gridpane.add(table, 1, 5,1,20 );
root.getChildren().addAll(gridpane);
primaryStage.setScene(scene);
primaryStage.show();
}
private class anyMethod extends TableCell <String, String>{
#SuppressWarnings("unchecked")
#Override
protected void updateItem(String item, boolean arg1) {
super.updateItem(item, arg1);
answerCol.setCellFactory(ComboBoxTableCell.<String, String>forTableColumn(namesChoiceList));
/**** I have to execute this commented code so that if the column cell has text "Multiple Choice" then
* it displays the comboBox otherwise it displays the text field in the Table View cell
if (item.equalsIgnoreCase("Multiple Choice")){
answerCol.setCellFactory(ComboBoxTableCell.<String, String>forTableColumn(namesChoiceList));
}
else{
//answerCol.setCellFactory(TextFieldTableCell.<String>forTableColumn());
}
****/
}
}
public static class Person {
private final SimpleStringProperty answers;
private final SimpleStringProperty answersType;
private Person(String answers, String answersType) {
this.answers = new SimpleStringProperty(answers);
this.answersType = new SimpleStringProperty(answersType);
}
public String getAnswers() {
return answers.get();
}
public void setAnswers(String answers) {
this.answers.set(answers);
}
public String getAnswersType() {
return answersType.get();
}
public void setAnswersType(String answersType) {
this.answersType.set(answersType);
}
}
}
Here is a sample for an EditingCell which renders a different control in the cell (TextEdit field or Checkbox) depending on the type of data represented by the Cell's backing field (String or Boolean). Complete executable code is available as a gist.
For your particular example, use the same concept except query the type for either a String => TextField or ObservableList => combobox. Also, for your particular sample, ChoiceBox may be a simpler control to use than ComboBox.
class EditingCell extends TableCell<NamedProperty, Object> {
private TextField textField;
private CheckBox checkBox;
public EditingCell() {}
#Override public void startEdit() {
if (!isEmpty()) {
super.startEdit();
if (getItem() instanceof Boolean) {
createCheckBox();
setText(null);
setGraphic(checkBox);
} else {
createTextField();
setText(null);
setGraphic(textField);
textField.selectAll();
}
}
}
#Override public void cancelEdit() {
super.cancelEdit();
if (getItem() instanceof Boolean) {
setText(getItem().toString());
} else {
setText((String) getItem());
}
setGraphic(null);
}
#Override public void updateItem(Object item, boolean empty) {
super.updateItem(item, empty);
if (empty) {
setText(null);
setGraphic(null);
} else {
if (isEditing()) {
if (getItem() instanceof Boolean) {
if (checkBox != null) {
checkBox.setSelected(getBoolean());
}
setText(null);
setGraphic(checkBox);
} else {
if (textField != null) {
textField.setText(getString());
}
setText(null);
setGraphic(textField);
}
} else {
setText(getString());
setGraphic(null);
}
}
}
private void createTextField() {
textField = new TextField(getString());
textField.setMinWidth(this.getWidth() - this.getGraphicTextGap()* 2);
textField.focusedProperty().addListener(new ChangeListener<Boolean>() {
#Override public void changed(ObservableValue<? extends Boolean> observable, Boolean oldValue, Boolean newValue) {
if (!newValue) {
commitEdit(textField.getText());
}
}
});
}
private void createCheckBox() {
checkBox = new CheckBox();
checkBox.setSelected(getBoolean());
checkBox.setMinWidth(this.getWidth() - this.getGraphicTextGap()* 2);
checkBox.focusedProperty().addListener(new ChangeListener<Boolean>() {
#Override public void changed(ObservableValue<? extends Boolean> observable, Boolean oldValue, Boolean newValue) {
if (!newValue) {
commitEdit(checkBox.isSelected());
}
}
});
}
private String getString() {
return getItem() == null ? "" : getItem().toString();
}
private Boolean getBoolean() {
return getItem() == null ? false : (Boolean) getItem();
}
}
Bellow is the working code for my question. Thank jewelsea
import javafx.application.Application;
import javafx.beans.property.SimpleStringProperty;
import javafx.collections.FXCollections;
import javafx.collections.ObservableList;
import javafx.geometry.Insets;
import javafx.scene.Scene;
import javafx.scene.control.ComboBox;
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.PropertyValueFactory;
import javafx.scene.layout.GridPane;
import javafx.scene.layout.StackPane;
import javafx.stage.Stage;
import javafx.util.Callback;
public class TableCellWithMultipleComponent extends Application {
#SuppressWarnings("rawtypes")
TableColumn answerTypeCol;
#SuppressWarnings("rawtypes")
TableColumn answerCol;
ObservableList<String> namesChoiceList;
#SuppressWarnings("rawtypes")
ComboBox comboBox;
TextField textField;
public static void main(String[] args) {
launch(args);
}
#SuppressWarnings({ "rawtypes", "unchecked" })
#Override
public void start(final Stage primaryStage) {
primaryStage.setTitle("Table Cell With Multiple Components");
TableView<Person> table = new TableView<Person>();
table.setEditable(true);
final ObservableList<Person> data =
FXCollections.observableArrayList(
new Person("A", "Multiple Choice"),
new Person("JOHN", "Free Text"),
new Person("123", "Free Text"),
new Person("D", "Multiple Choice")
);
GridPane gridpane = new GridPane();
gridpane.setPadding(new Insets(5));
gridpane.setHgap(5);
gridpane.setVgap(5);
namesChoiceList = FXCollections.observableArrayList("A", "B", "C", "D", "INVALID_ANSWER", "NO_ANSWER");
answerCol = new TableColumn();
answerCol.setText("Answers");
answerCol.setMinWidth(210);
answerCol.setEditable(true);
answerCol.setCellValueFactory(new PropertyValueFactory("answers"));
answerCol.setCellFactory( new Callback<TableColumn<String, String>, TableCell<String, String>>() {
#Override
public TableCell<String, String> call(TableColumn<String, String> arg0) {
return new anyMethod();
}
});
answerTypeCol = new TableColumn();
answerTypeCol.setText("Answers Type");
answerTypeCol.setMinWidth(210);
answerTypeCol.setEditable(true);
answerTypeCol.setCellValueFactory(new PropertyValueFactory("answersType"));
table.setItems(data);
table.getColumns().addAll(answerCol, answerTypeCol);
StackPane root = new StackPane();
Scene scene =new Scene(root, 500, 550);
gridpane.add(table, 1, 5,1,20 );
root.getChildren().addAll(gridpane);
primaryStage.setScene(scene);
primaryStage.show();
}
private class anyMethod extends TableCell <String, String>{
#SuppressWarnings({ "unchecked", "rawtypes" })
public anyMethod(){
comboBox = new ComboBox();
textField = new TextField();
comboBox.setItems(namesChoiceList);
}
#Override
protected void updateItem(String item, boolean empty) {
super.updateItem(item, empty);
if (empty) {
setText(null);
setGraphic(null);
System.out.println("In empty");
} else {
if( getTableView().getColumns().get(1).getCellData(getIndex()).toString().startsWith("M")){
System.out.println("Making ComboBox");
setGraphic(comboBox);
}
else{
setGraphic(textField);
}
}
}
}
public static class Person {
private final SimpleStringProperty answers;
private final SimpleStringProperty answersType;
private Person(String answers, String answersType) {
this.answers = new SimpleStringProperty(answers);
this.answersType = new SimpleStringProperty(answersType);
}
public String getAnswers() {
return answers.get();
}
public void setAnswers(String answers) {
this.answers.set(answers);
}
public String getAnswersType() {
return answersType.get();
}
public void setAnswersType(String answersType) {
this.answersType.set(answersType);
}
}
}