I have two TableView (table1 and table2) one next to the other
What I need to do is:
When you select an item in table1 the corresponding item is selected in table2
So far so good was easy, but I need to reproduce the same effect in table2, and it is when
arises the NPE the listener applied in table1 conflict with the listener of table2.
I tried to create an event in focusedProperty () but without success :(
I made a test application to post here, as it would not fit all code follows
download link TableView - Teste.
This feels like it might be a bug, but I don't have time to experiment with it properly. A workaround seems to be to update the selection in the "other" table inside a Platform.runLater(). You need to be careful not to create an infinite number of these calls by checking the selection really is different:
import javafx.application.Application;
import javafx.application.Platform;
import javafx.beans.property.SimpleStringProperty;
import javafx.beans.property.StringProperty;
import javafx.beans.value.ChangeListener;
import javafx.scene.Scene;
import javafx.scene.control.TableColumn;
import javafx.scene.control.TableView;
import javafx.scene.layout.HBox;
import javafx.stage.Stage;
public class TwoTableTest extends Application {
private ChangeListener<Number> table1SelectionListener ;
private ChangeListener<Number> table2SelectionListener ;
#Override
public void start(Stage primaryStage) {
TableView<Person> table1 = createTableView() ;
TableView<Person> table2 = createTableView() ;
table1.getSelectionModel().select(0);
table2.getSelectionModel().select(0);
table1SelectionListener = (obs, oldIndex, newIndex) -> {
int table1SelectedIndex = table1.getSelectionModel().getSelectedIndex() ;
int table2SelectedIndex = table2.getSelectionModel().getSelectedIndex() ;
if (table1SelectedIndex != table2SelectedIndex) {
Platform.runLater(() -> table2.getSelectionModel().select(table1SelectedIndex));
}
};
table2SelectionListener = (obs, oldIndex, newIndex) -> {
int table1SelectedIndex = table1.getSelectionModel().getSelectedIndex() ;
int table2SelectedIndex = table2.getSelectionModel().getSelectedIndex() ;
if (table1SelectedIndex != table2SelectedIndex) {
Platform.runLater(() -> table1.getSelectionModel().select(table2SelectedIndex));
}
};
table1.getSelectionModel().selectedIndexProperty().addListener(table1SelectionListener);
table2.getSelectionModel().selectedIndexProperty().addListener(table2SelectionListener);
HBox root = new HBox(5, table1, table2);
Scene scene = new Scene(root, 800, 600);
primaryStage.setScene(scene);
primaryStage.show();
}
private TableView<Person> createTableView() {
TableView<Person> table = new TableView<>();
TableColumn<Person, String> firstNameCol = new TableColumn<>("First Name");
firstNameCol.setCellValueFactory(data -> data.getValue().firstNameProperty());
TableColumn<Person, String> lastNameCol = new TableColumn<>("Last Name");
lastNameCol.setCellValueFactory(data -> data.getValue().lastNameProperty());
TableColumn<Person, String> emailCol = new TableColumn<>("Email");
emailCol.setCellValueFactory(data -> data.getValue().emailProperty());
table.getColumns().addAll(firstNameCol, lastNameCol);
table.getItems().addAll(
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")
);
return table ;
}
public static void main(String[] args) {
launch(args);
}
public static class Person {
private final StringProperty firstName;
private final StringProperty lastName;
private final StringProperty email ;
Person(String firstName, String lastName, String email) {
this.firstName = new SimpleStringProperty(this, "firstName",
firstName);
this.lastName = new SimpleStringProperty(this, "lastName", lastName);
this.email = new SimpleStringProperty(this, "email", email);
}
public String getFirstName() {
return firstName.get();
}
public void setFirstName(String firstName) {
this.firstName.set(firstName);
}
public StringProperty firstNameProperty() {
return firstName;
}
public String getLastName() {
return lastName.get();
}
public void setLastName(String lastName) {
this.lastName.set(lastName);
}
public StringProperty lastNameProperty() {
return lastName;
}
public String getEmail() {
return email.get();
}
public void setEmail(String email) {
this.email.set(email);
}
public StringProperty emailProperty() {
return email ;
}
#Override
public String toString() {
return firstName.get() + " " + lastName.get();
}
}
}
Related
I am going through the Oracle tutorial with tables, and have implemented my own tabPane into this code. What I want is for the table to be a global table, so that when I go onto tab 2, it is the exact same as it is on Tab 1, just only that i can edit certain columns in tab 2 compared to editing it all in tab 1.
Is there anyway this can be done instead of basically duplicating code and creating a second table, which i believe is tedious as I don't want two tables, I want one global table.
public class TableSample extends Application {
private TableView<Person> table = new TableView<Person>();
private final ObservableList<Person> data =
FXCollections.observableArrayList(
new Person("Jacob", "Smith", "jacob.smith#example.com"),
new Person("Isabella", "Johnson", "isabella.johnson#example.com"));
public static void main(String[] args) {
launch(args);
}
private TableView table = new TableView();
public void start(Stage stage) {
Scene scene = new Scene(new Group());
TabPane tabPane = new TabPane();
Tab tab1 = new Tab("Tab 1");
tabPane.getTabs().add(tab1);
Tab tab2 = new Tab("Tab 2");
tabPane.getTabs().add(tab2);
table.setEditable(true);
TableColumn firstNameCol = new TableColumn("First Name");
firstNameCol.setCellValueFactory(
new PropertyValueFactory<Person, String>("firstName"));
firstNameCol.setCellFactory(TextFieldTableCell.forTableColumn());
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.setCellValueFactory(
new PropertyValueFactory<Person, String>("lastName"));
lastNameCol.setCellFactory(TextFieldTableCell.forTableColumn());
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.setCellValueFactory(
new PropertyValueFactory<Person, String>("email"));
emailCol.setCellFactory(TextFieldTableCell.forTableColumn());
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 VBox vbox = new VBox();
vbox.getChildren().addAll(tabPane, table);
((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);
}
}
}
Your code structure is already setup correctly. You just need to add Nodes to the different Tabs
Key Code: You need to add a root node to each Tab. Each root node will hold the other nodes that give each Tab it looks.
Tab tab1 = new Tab("Tab 1");
Label tab1Label = new Label("Use tab1Root to add nodes for tab1!");
StackPane tab1Root = new StackPane(tab1Label);
tab1Root.setPrefSize(500, 500);
tab1.setContent(tab1Root);
tabPane.getTabs().add(tab1);
Tab tab2 = new Tab("Tab 2");
Button tab2Button = new Button("Press Me!");
tab2Button.setOnAction((event) -> {
System.out.println("Use tab2Root to add nodes for tab2!");
});
StackPane tab2Root = new StackPane(tab2Button);
tab2Root.setPrefSize(500, 500);
tab2.setContent(tab2Root);
tabPane.getTabs().add(tab2);
Full Code
import javafx.application.Application;
import javafx.beans.property.SimpleStringProperty;
import javafx.collections.FXCollections;
import javafx.collections.ObservableList;
import javafx.event.EventHandler;
import javafx.scene.Group;
import javafx.scene.Scene;
import javafx.scene.control.Button;
import javafx.scene.control.Label;
import javafx.scene.control.Tab;
import javafx.scene.control.TabPane;
import javafx.scene.control.TableColumn;
import javafx.scene.control.TableView;
import javafx.scene.control.TreeTableColumn.CellEditEvent;
import javafx.scene.control.cell.PropertyValueFactory;
import javafx.scene.control.cell.TextFieldTableCell;
import javafx.scene.layout.StackPane;
import javafx.scene.layout.VBox;
import javafx.stage.Stage;
public class TestingGround extends Application
{
private TableView<Person> table = new TableView<Person>();
private final ObservableList<Person> data
= FXCollections.observableArrayList(
new Person("Jacob", "Smith", "jacob.smith#example.com"),
new Person("Isabella", "Johnson", "isabella.johnson#example.com"));
public static void main(String[] args)
{
launch(args);
}
public void start(Stage stage)
{
Scene scene = new Scene(new Group());
TabPane tabPane = new TabPane();
Tab tab1 = new Tab("Tab 1");
Label tab1Label = new Label("Use tab1Root to add nodes for tab1!");
StackPane tab1Root = new StackPane(tab1Label);
tab1Root.setPrefSize(500, 500);
tab1.setContent(tab1Root);
tabPane.getTabs().add(tab1);
Tab tab2 = new Tab("Tab 2");
Button tab2Button = new Button("Press Me!");
tab2Button.setOnAction((event) -> {
System.out.println("Use tab2Root to add nodes for tab2!");
});
StackPane tab2Root = new StackPane(tab2Button);
tab2Root.setPrefSize(500, 500);
tab2.setContent(tab2Root);
tabPane.getTabs().add(tab2);
table.setEditable(true);
TableColumn firstNameCol = new TableColumn("First Name");
firstNameCol.setCellValueFactory(
new PropertyValueFactory<Person, String>("firstName"));
firstNameCol.setCellFactory(TextFieldTableCell.forTableColumn());
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.setCellValueFactory(
new PropertyValueFactory<Person, String>("lastName"));
lastNameCol.setCellFactory(TextFieldTableCell.forTableColumn());
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.setCellValueFactory(
new PropertyValueFactory<Person, String>("email"));
emailCol.setCellFactory(TextFieldTableCell.forTableColumn());
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 VBox vbox = new VBox();
vbox.getChildren().addAll(tabPane, table);
((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);
}
}
}
I am trying to wrap the text in the ComboBox but I cannot really manage it.
I need the wrapped text in the editor part, not in the dropdown part of the comboBox. I see that the editor is a TextField, and its text cannot really be wrapped or?
Is there any trick or solution to have a nicely wrapped text so if I have a really long text I can see it every time?
So instead of displaying the ... I want the whole text to be displayed.
Code part I don't know if I should add since it is really simple table cell, and a ComboBox set as Graphics, but if it helps I will edit the question.
Note: Setting the colum wider so the text can fit, is not a solution!
Here is a screenshot:
I am bit confused about the question. You want to show the wrapped text while editing or while in non edit mode.? Because if you are in editing mode you cannot see ... in text box.
So I assume you are asking for showing the wrapped text after selecting from popup list and the combo is not in edit mode.We can fix that using buttonCell.
If this is what you are not asking for, then a Minimal, Complete, and Verifiable example can help me to look into the actual issue.
Please refer to the below code of how to show the wrapped text.
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.*;
import javafx.scene.control.cell.PropertyValueFactory;
import javafx.scene.layout.StackPane;
import javafx.scene.layout.VBox;
import javafx.scene.text.Font;
import javafx.scene.text.Text;
import javafx.stage.Stage;
import javafx.util.Callback;
public class TableViewComboBoxCell extends Application {
private TableView<Person> table = new TableView<Person>();
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"));
public static void main(String[] args) {
launch(args);
}
private final ObservableList<String> comboList = FXCollections.observableArrayList("First big sentence with very long text to check the text wrap",
"Second big sentence with very long text to check the text wrap",
"Third big sentence with very long text to check the text wrap",
"Fourth big sentence with very long text to check the text wrap");
#Override
public void start(Stage stage) {
for (int i = 0; i < 50; i++) {
if(i%5==0){
data.add(new Person("Name " + i, "Last " + i, "Mail " + i, comboList.get(0)));
}else {
data.add(new Person("Name " + i, "Last " + i, "Mail " + i));
}
}
Scene scene = new Scene(new StackPane());
stage.setTitle("Table View Sample");
stage.setWidth(650);
stage.setHeight(500);
final Label label = new Label("Address Book");
label.setFont(new Font("Arial", 20));
TableColumn<Person, String> firstNameCol = new TableColumn<>("First Name");
firstNameCol.setMinWidth(100);
firstNameCol
.setCellValueFactory(new PropertyValueFactory<Person, String>(
"firstName"));
TableColumn<Person, String> lastNameCol = new TableColumn<>("Last Name");
lastNameCol.setMinWidth(100);
lastNameCol
.setCellValueFactory(new PropertyValueFactory<Person, String>(
"lastName"));
TableColumn<Person, String> emailCol = new TableColumn<>("Email");
emailCol.setMinWidth(200);
emailCol.setCellValueFactory(new PropertyValueFactory<Person, String>(
"email"));
TableColumn<Person, String> comboCol = new TableColumn<>("Combo");
comboCol.setMinWidth(200);
comboCol.setCellValueFactory(new PropertyValueFactory<Person, String>(
"combo"));
comboCol.setCellFactory(new Callback<TableColumn<Person, String>, TableCell<Person, String>>() {
#Override
public TableCell<Person, String> call(TableColumn<Person, String> param) {
return new TableCell<Person, String>() {
private ComboBox<String> combo;
#Override
protected void updateItem(String item, boolean empty) {
super.updateItem(item, empty);
getCombo().getSelectionModel().clearSelection();
if (!empty) {
getCombo().setValue(item);
setGraphic(getCombo());
} else {
setGraphic(null);
}
}
private ComboBox<String> getCombo() {
if (combo == null) {
combo = new ComboBox<>();
combo.setItems(comboList);
combo.getSelectionModel().selectedItemProperty().addListener((obs, old, newVal) -> {
((Person) getTableRow().getItem()).setCombo(newVal);
});
combo.setButtonCell(new ListCell<String>() {
private Text textLbl;
#Override
protected void updateItem(String item, boolean empty) {
super.updateItem(item, empty);
setGraphic(null);
if (!empty) {
getTextLbl().setText(item);
setGraphic(getTextLbl());
}
}
private Text getTextLbl(){
if(textLbl ==null){
textLbl = new Text();
textLbl.wrappingWidthProperty().bind(this.widthProperty().subtract(10));
}
return textLbl;
}
});
}
return combo;
}
};
}
});
table.setItems(data);
table.getColumns().addAll(firstNameCol, lastNameCol, emailCol, comboCol);
final VBox vbox = new VBox();
vbox.setSpacing(5);
vbox.setPadding(new Insets(10, 0, 0, 10));
vbox.getChildren().addAll(label, table);
((StackPane) 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 final SimpleStringProperty combo;
private Person(String fName, String lName, String email) {
this(fName,lName,email,null);
}
private Person(String fName, String lName, String email, String comboStr) {
this.firstName = new SimpleStringProperty(fName);
this.lastName = new SimpleStringProperty(lName);
this.email = new SimpleStringProperty(email);
this.combo = new SimpleStringProperty(comboStr);
}
public String getFirstName() {
return firstName.get();
}
public SimpleStringProperty firstNameProperty() {
return firstName;
}
public void setFirstName(String firstName) {
this.firstName.set(firstName);
}
public String getLastName() {
return lastName.get();
}
public SimpleStringProperty lastNameProperty() {
return lastName;
}
public void setLastName(String lastName) {
this.lastName.set(lastName);
}
public String getEmail() {
return email.get();
}
public SimpleStringProperty emailProperty() {
return email;
}
public void setEmail(String email) {
this.email.set(email);
}
public String getCombo() {
return combo.get();
}
public SimpleStringProperty comboProperty() {
return combo;
}
public void setCombo(String combo) {
this.combo.set(combo);
}
}
}
And the output is as below:
For DTO I use POJO. So to make bidirectional binding I create adapter. I mean, something like that:
POJO:
public class Temp{
private BigDecimal weight;
private final PropertyChangeSupport propertyChangeSupport;
public Temp() {
this.propertyChangeSupport = new PropertyChangeSupport(this);
}
public void addPropertyChangeListener(PropertyChangeListener listener) {
propertyChangeSupport.addPropertyChangeListener(listener);
}
public BigDecimal getWeight() {
return weight;
}
public void setWeight(BigDecimal weight) {
BigDecimal pv = this.weight;
this.weight = weight;
propertyChangeSupport.firePropertyChange("weight", pv, weight);
}
}
I have the following adapter:
public class TempAdapter {
private ObjectProperty<BigDecimal> weightProperty;
public TempAdapter(Temp temp) {
try {
weightProperty=new JavaBeanObjectPropertyBuilder<BigDecimal>().bean(temp).name("weight").build();
weightProperty.addListener(new ChangeListener<BigDecimal>() {
#Override
public void changed(ObservableValue<? extends BigDecimal> ov, BigDecimal t, BigDecimal t1) {
....
}
});
} catch (NoSuchMethodException ex) {
Logger.getLogger(this.getClass().getName()).log(Level.SEVERE, null, ex);
}
}
public ObjectProperty<BigDecimal> getWeightProperty() {
return weightProperty;
}
However, I can't understand how to use this adapter with TableView. The reason I want to use adapter for TableView is that otherwise we will have to duplicate the code of the adapter in TableView if we using POJO for DTO with TableView.
As I understand for every row in TableView we must create a new Instance of Adapter and I can't understand how to do it.
Solution without an adapter class
First note you don't necessarily need an adapter class; you can just create the JavaBeanProperty instances where you need them: in this case in the cell value factory for the table. If there is just one (or perhaps two) places in the UI where you need to bind directly to a JavaFX Property corresponding to properties in your POJO, then this is probably the way to go.
Here's a complete example of this technique, using the usual Oracle Person table example. In this example, there is no adapter class: the table just creates the JavaBeanStringProperty adapters in the cell value factory. There is an edit form, which just interacts with the POJO class directly.
import java.beans.PropertyChangeListener;
import java.beans.PropertyChangeSupport;
import javafx.application.Application;
import javafx.beans.property.adapter.JavaBeanStringProperty;
import javafx.beans.property.adapter.JavaBeanStringPropertyBuilder;
import javafx.geometry.HPos;
import javafx.geometry.Insets;
import javafx.geometry.Pos;
import javafx.scene.Scene;
import javafx.scene.control.Button;
import javafx.scene.control.Label;
import javafx.scene.control.TableColumn;
import javafx.scene.control.TableView;
import javafx.scene.control.TextField;
import javafx.scene.control.cell.TextFieldTableCell;
import javafx.scene.layout.BorderPane;
import javafx.scene.layout.GridPane;
import javafx.scene.layout.HBox;
import javafx.stage.Modality;
import javafx.stage.Stage;
import javafx.stage.StageStyle;
public class PojoTable extends Application {
#Override
public void start(Stage primaryStage) {
TableView<Person> table = new TableView<>();
table.setEditable(true);
TableColumn<Person, String> firstNameColumn = createColumn("First Name", "firstName");
TableColumn<Person, String> lastNameColumn = createColumn("Last Name", "lastName");
table.getColumns().add(firstNameColumn);
table.getColumns().add(lastNameColumn);
Button button = new Button("Show data");
button.setOnAction(e -> {
table.getItems().stream().map(person -> person.getFirstName() + " " + person.getLastName())
.forEach(System.out::println);
System.out.println();
});
Button edit = new Button("Edit");
edit.disableProperty().bind(table.getSelectionModel().selectedItemProperty().isNull());
edit.setOnAction(e -> edit(table.getSelectionModel().getSelectedItem(), primaryStage));
table.getItems().addAll(
new Person("Jacob", "Smith"),
new Person("Isabella", "Johnson"),
new Person("Ethan", "Williams"),
new Person("Emma", "Jones"),
new Person("Michael", "Brown")
);
HBox buttons = new HBox(10, button, edit);
buttons.setAlignment(Pos.CENTER);
BorderPane root = new BorderPane(table, null, null, buttons, null);
BorderPane.setAlignment(buttons, Pos.CENTER);
BorderPane.setMargin(buttons, new Insets(10));
root.setPadding(new Insets(10));
primaryStage.setScene(new Scene(root, 600, 600));
primaryStage.show();
}
private void edit(Person person, Stage primaryStage) {
GridPane editPane = new GridPane();
TextField firstNameField = new TextField(person.getFirstName());
TextField lastNameField = new TextField(person.getLastName());
Button okButton = new Button("OK");
Button cancelButton = new Button("Cancel");
HBox buttons = new HBox(10, okButton, cancelButton);
editPane.addRow(0, new Label("First Name:"), firstNameField);
editPane.addRow(1, new Label("Last Name:"), lastNameField);
editPane.add(buttons, 0, 2, 2, 1);
GridPane.setHalignment(buttons, HPos.CENTER);
GridPane.setMargin(buttons, new Insets(10));
editPane.setPadding(new Insets(10));
Scene scene = new Scene(editPane);
Stage stage = new Stage();
stage.setScene(scene);
stage.initOwner(primaryStage);
stage.initModality(Modality.APPLICATION_MODAL);
stage.initStyle(StageStyle.UNDECORATED);
cancelButton.setOnAction(e -> stage.hide());
okButton.setOnAction(e -> {
person.setFirstName(firstNameField.getText());
person.setLastName(lastNameField.getText());
stage.hide();
});
stage.show();
}
private TableColumn<Person, String> createColumn(String title, String property) {
TableColumn<Person, String> col = new TableColumn<>(title);
col.setCellValueFactory(cellData -> {
Person p = cellData.getValue();
try {
JavaBeanStringProperty prop = new JavaBeanStringPropertyBuilder()
.bean(p)
.name(property)
.build();
return prop;
} catch (Exception e) {
throw new RuntimeException(e);
}
});
col.setCellFactory(TextFieldTableCell.forTableColumn());
return col ;
}
public static class Person {
private String firstName ;
private String lastName ;
private PropertyChangeSupport support ;
public Person(String firstName, String lastName) {
this.firstName = firstName ;
this.lastName = lastName ;
support = new PropertyChangeSupport(this);
}
public String getFirstName() {
return firstName;
}
public void setFirstName(String firstName) {
String previous = this.firstName ;
this.firstName = firstName;
support.firePropertyChange("firstName", previous, firstName);
}
public String getLastName() {
return lastName;
}
public void setLastName(String lastName) {
String previous = this.lastName ;
this.lastName = lastName;
support.firePropertyChange("lastName", previous, lastName);
}
public void addPropertyChangeListener(PropertyChangeListener listener) {
support.addPropertyChangeListener(listener);
}
public void removePropertyChangeListener(PropertyChangeListener listener) {
support.removePropertyChangeListener(listener);
}
}
public static void main(String[] args) {
launch(args);
}
}
Solution using an adapter class
Note that in the above example, the text fields in the editor can't use bindings directly with the POJO class (because it doesn't expose any JavaFX properties); if you wanted to do so you could create more JavaBeanStringPropertys for the purpose, but that would end up duplicating code. If you wanted to be able to do this, then it might become beneficial to use an adapter class. Here is what the code might look like using this solution. Note that now the adapter class exposes JavaFX properties, so the table's cell value factory can just map directly to those properties: the creation of the JavaBeanStringPropertys is encapsulated in one place (the adapter class):
import java.beans.PropertyChangeListener;
import java.beans.PropertyChangeSupport;
import java.util.Arrays;
import java.util.List;
import java.util.function.Function;
import javafx.application.Application;
import javafx.beans.property.StringProperty;
import javafx.beans.property.adapter.JavaBeanStringPropertyBuilder;
import javafx.geometry.HPos;
import javafx.geometry.Insets;
import javafx.geometry.Pos;
import javafx.scene.Scene;
import javafx.scene.control.Button;
import javafx.scene.control.Label;
import javafx.scene.control.TableColumn;
import javafx.scene.control.TableView;
import javafx.scene.control.TextField;
import javafx.scene.control.cell.TextFieldTableCell;
import javafx.scene.layout.BorderPane;
import javafx.scene.layout.GridPane;
import javafx.scene.layout.HBox;
import javafx.stage.Modality;
import javafx.stage.Stage;
import javafx.stage.StageStyle;
public class PojoTable extends Application {
#Override
public void start(Stage primaryStage) {
TableView<PersonAdapter> table = new TableView<>();
table.setEditable(true);
TableColumn<PersonAdapter, String> firstNameColumn = createColumn("First Name", PersonAdapter::firstNameProperty);
TableColumn<PersonAdapter, String> lastNameColumn = createColumn("Last Name", PersonAdapter::lastNameProperty);
table.getColumns().add(firstNameColumn);
table.getColumns().add(lastNameColumn);
List<Person> data = Arrays.asList(
new Person("Jacob", "Smith"),
new Person("Isabella", "Johnson"),
new Person("Ethan", "Williams"),
new Person("Emma", "Jones"),
new Person("Michael", "Brown")
);
Button button = new Button("Show data");
button.setOnAction(e -> {
data.stream().map(person -> person.getFirstName() + " " + person.getLastName())
.forEach(System.out::println);
System.out.println();
});
Button edit = new Button("Edit");
edit.disableProperty().bind(table.getSelectionModel().selectedItemProperty().isNull());
edit.setOnAction(e -> edit(table.getSelectionModel().getSelectedItem(), primaryStage));
data.stream().map(PersonAdapter::new).forEach(table.getItems()::add);
HBox buttons = new HBox(10, button, edit);
buttons.setAlignment(Pos.CENTER);
BorderPane root = new BorderPane(table, null, null, buttons, null);
BorderPane.setAlignment(buttons, Pos.CENTER);
BorderPane.setMargin(buttons, new Insets(10));
root.setPadding(new Insets(10));
primaryStage.setScene(new Scene(root, 600, 600));
primaryStage.show();
}
private void edit(PersonAdapter person, Stage primaryStage) {
GridPane editPane = new GridPane();
TextField firstNameField = new TextField();
firstNameField.textProperty().bindBidirectional(person.firstNameProperty());
TextField lastNameField = new TextField();
lastNameField.textProperty().bindBidirectional(person.lastNameProperty());
Button okButton = new Button("OK");
HBox buttons = new HBox(10, okButton);
editPane.addRow(0, new Label("First Name:"), firstNameField);
editPane.addRow(1, new Label("Last Name:"), lastNameField);
editPane.add(buttons, 0, 2, 2, 1);
GridPane.setHalignment(buttons, HPos.CENTER);
GridPane.setMargin(buttons, new Insets(10));
editPane.setPadding(new Insets(10));
Scene scene = new Scene(editPane);
Stage stage = new Stage();
stage.setScene(scene);
stage.initOwner(primaryStage);
stage.initModality(Modality.APPLICATION_MODAL);
stage.initStyle(StageStyle.UNDECORATED);
okButton.setOnAction(e -> {
stage.hide();
});
stage.show();
}
private TableColumn<PersonAdapter, String> createColumn(String title, Function<PersonAdapter, StringProperty> property) {
TableColumn<PersonAdapter, String> col = new TableColumn<>(title);
col.setCellValueFactory(cellData -> property.apply(cellData.getValue()));
col.setCellFactory(TextFieldTableCell.forTableColumn());
return col ;
}
public static class Person {
private String firstName ;
private String lastName ;
private PropertyChangeSupport support ;
public Person(String firstName, String lastName) {
this.firstName = firstName ;
this.lastName = lastName ;
support = new PropertyChangeSupport(this);
}
public String getFirstName() {
return firstName;
}
public void setFirstName(String firstName) {
String previous = this.firstName ;
this.firstName = firstName;
support.firePropertyChange("firstName", previous, firstName);
}
public String getLastName() {
return lastName;
}
public void setLastName(String lastName) {
String previous = this.lastName ;
this.lastName = lastName;
support.firePropertyChange("lastName", previous, lastName);
}
public void addPropertyChangeListener(PropertyChangeListener listener) {
support.addPropertyChangeListener(listener);
}
public void removePropertyChangeListener(PropertyChangeListener listener) {
support.removePropertyChangeListener(listener);
}
}
public static class PersonAdapter {
private final Person person ;
private final StringProperty firstName ;
private final StringProperty lastName ;
public PersonAdapter(Person person) {
this.person = person ;
try {
this.firstName = new JavaBeanStringPropertyBuilder()
.bean(person)
.name("firstName")
.build();
this.lastName = new JavaBeanStringPropertyBuilder()
.bean(person)
.name("lastName")
.build();
} catch (Exception e) {
throw new RuntimeException(e);
}
}
public Person getPerson() {
return person ;
}
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 static void main(String[] args) {
launch(args);
}
}
The one possible disadvantage to this approach is that changes to the underlying list (data in the simple example) will not propagate to the table (this means adding or removing elements from data will not change the table; calling setFirstName or setLastName on the existing elements of the table will allow for updates). For techniques to manage this, see Best practice to decorate an ObservableList and retain change events
My problem is that my JavaFX TableView cannot display my String that is crypted with SHA-256. It works with a normal string. Here my code for the my scramble method.
private String scrambleName(String name, String surname)
{
MessageDigest messageDigest;
String cryptedName;
StringBuilder builder = new StringBuilder();
builder.append(name);
builder.append(" ");
builder.append(surname);
String fullName = builder.toString();
try
{
messageDigest = MessageDigest.getInstance("SHA-256");
messageDigest.update(fullName.getBytes());
cryptedName = new String(messageDigest.digest());
return cryptedName;
} catch (NoSuchAlgorithmException ex)
{
Logger.getLogger(Scrambler.class.getName()).log(Level.SEVERE, null, ex);
}
return null;
}
This method returns a string for my scrambled vote object.
public class ScrambledVote
{
private int key;
private String scrambledName;
private int age;
private String party;
public ScrambledVote(int key, String scrambledName, int age, String party)
{
this.key = key;
this.scrambledName = scrambledName;
this.age = age;
this.party = party;
}
//Getter Setter
I load the data from a file. The command line output works fine. But the TableView throws an exception.
Exception in thread "JavaFX Application Thread" java.lang.ArrayIndexOutOfBoundsException: 0
at com.sun.javafx.font.directwrite.DWGlyphLayout.getIndices(DWGlyphLayout.java:198)
at com.sun.javafx.font.directwrite.DWGlyphLayout.renderShape(DWGlyphLayout.java:401)
at com.sun.javafx.font.directwrite.DWGlyphLayout.layout(DWGlyphLayout.java:154)
at com.sun.javafx.text.PrismTextLayout.shape(PrismTextLayout.java:832)
at com.sun.javafx.text.PrismTextLayout.layout(PrismTextLayout.java:1062)
at com.sun.javafx.text.PrismTextLayout.ensureLayout(PrismTextLayout.java:221)
at com.sun.javafx.text.PrismTextLayout.getBounds(PrismTextLayout.java:244)
at com.sun.javafx.scene.control.skin.Utils.computeTextHeight(Utils.java:129)
at com.sun.javafx.scene.control.skin.LabeledSkinBase.computePrefHeight(LabeledSkinBase.java:832)
at javafx.scene.control.Control.computePrefHeight(Control.java:543)
at javafx.scene.Parent.prefHeight(Parent.java:918)
at javafx.scene.layout.Region.prefHeight(Region.java:1438)
at com.sun.javafx.scene.control.skin.TableRowSkinBase.computePrefHeight(TableRowSkinBase.java:544)
at javafx.scene.control.Control.computePrefHeight(Control.java:543)
at javafx.scene.Parent.prefHeight(Parent.java:924)
at javafx.scene.layout.Region.prefHeight(Region.java:1438)
at com.sun.javafx.scene.control.skin.VirtualFlow.resizeCellSize(VirtualFlow.java:1782)
at com.sun.javafx.scene.control.skin.VirtualFlow.addTrailingCells(VirtualFlow.java:1212)
at com.sun.javafx.scene.control.skin.VirtualFlow.layoutChildren(VirtualFlow.java:1102)
at javafx.scene.Parent.layout(Parent.java:1076)
at javafx.scene.Parent.layout(Parent.java:1082)
at javafx.scene.Parent.layout(Parent.java:1082)
at javafx.scene.Scene.doLayoutPass(Scene.java:576)
at javafx.scene.Scene$ScenePulseListener.pulse(Scene.java:2386)
at com.sun.javafx.tk.Toolkit$3.run(Toolkit.java:321)
at com.sun.javafx.tk.Toolkit$3.run(Toolkit.java:319)
at java.security.AccessController.doPrivileged(Native Method)
at com.sun.javafx.tk.Toolkit.runPulse(Toolkit.java:319)
at com.sun.javafx.tk.Toolkit.firePulse(Toolkit.java:348)
at com.sun.javafx.tk.quantum.QuantumToolkit.pulse(QuantumToolkit.java:479)
at com.sun.javafx.tk.quantum.QuantumToolkit.pulse(QuantumToolkit.java:460)
at com.sun.javafx.tk.quantum.QuantumToolkit$13.run(QuantumToolkit.java:327)
at com.sun.glass.ui.InvokeLaterDispatcher$Future.run(InvokeLaterDispatcher.java:95)
at com.sun.glass.ui.win.WinApplication._runLoop(Native Method)
at com.sun.glass.ui.win.WinApplication.access$300(WinApplication.java:39)
at com.sun.glass.ui.win.WinApplication$4$1.run(WinApplication.java:112)
at java.lang.Thread.run(Thread.java:745)
How can my TableView display this kind of scrambled String?
#FXML
protected void loadFile()
{
allScrambledVotes.clear();
HashTable hashTable = new HashTable(txtPath.getText());
LinkList[] allVotes = hashTable.getArray();
Scrambler scrambler = new Scrambler();
int k = 0;
for (LinkList vote : allVotes)
{
for (int j = 0; j < vote.getSize(); j++)
{
Vote v = vote.findVote(j);
scrambler.put(k, v);
allScrambledVotes.add(scrambler.get(k));
System.out.println(allScrambledVotes.get(k));
k++;
}
}
table.setItems(allScrambledVotes);
System.out.println("Hello World");
}
I forget my loadFile method. I load the data from a hashtable with my own functions. The error occurs after the Hello World sysout.
The digest() method returns an array of bytes: these are not the ascii values of the string representation of the digest. You're getting the error (I think) because JavaFX is unable to render a character for some of the byte values you have.
Instead of
cryptedName = new String(messageDigest.digest());
return cryptedName;
try something along the lines of
StringBuilder cryptedName = new StringBuilder();
for (byte b : messageDigest.digest()) {
cryptedName.append(String.format("%02x", b));
}
return cryptedName.toString();
Here is a complete example:
import java.security.MessageDigest;
import java.security.NoSuchAlgorithmException;
import javafx.application.Application;
import javafx.beans.binding.StringBinding;
import javafx.beans.property.ReadOnlyStringWrapper;
import javafx.beans.property.SimpleStringProperty;
import javafx.collections.FXCollections;
import javafx.collections.ObservableList;
import javafx.geometry.Insets;
import javafx.scene.Group;
import javafx.scene.Scene;
import javafx.scene.control.Label;
import javafx.scene.control.TableColumn;
import javafx.scene.control.TableView;
import javafx.scene.control.cell.PropertyValueFactory;
import javafx.scene.layout.VBox;
import javafx.scene.text.Font;
import javafx.stage.Stage;
;
public class TableViewSample extends Application {
private final 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")
);
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(500);
final Label label = new Label("Address Book");
label.setFont(new Font("Arial", 20));
table.setEditable(true);
TableColumn<Person, String> firstNameCol = new TableColumn<>("First Name");
firstNameCol.setMinWidth(100);
firstNameCol.setCellValueFactory(
new PropertyValueFactory<>("firstName"));
TableColumn<Person, String> lastNameCol = new TableColumn<>("Last Name");
lastNameCol.setMinWidth(100);
lastNameCol.setCellValueFactory(
new PropertyValueFactory<>("lastName"));
TableColumn<Person, String> emailCol = new TableColumn<>("Email");
emailCol.setMinWidth(200);
emailCol.setCellValueFactory(
new PropertyValueFactory<>("email"));
TableColumn<Person, String> digestCol = new TableColumn<>("Digest");
digestCol.setMinWidth(200);
digestCol.setCellValueFactory(
new PropertyValueFactory<>("digestedName"));
table.setItems(data);
table.getColumns().addAll(firstNameCol, lastNameCol, emailCol, digestCol);
final VBox vbox = new VBox();
vbox.setSpacing(5);
vbox.setPadding(new Insets(10, 0, 0, 10));
vbox.getChildren().addAll(label, table);
((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 final MessageDigest digest ;
private final ReadOnlyStringWrapper digestedName ;
private Person(String fName, String lName, String email) {
this.firstName = new SimpleStringProperty(fName);
this.lastName = new SimpleStringProperty(lName);
this.email = new SimpleStringProperty(email);
try {
digest = MessageDigest.getInstance("SHA-256");
} catch (NoSuchAlgorithmException e) {
throw new Error(e);
}
this.digestedName = new ReadOnlyStringWrapper();
digestedName.bind(new StringBinding() {
{
bind(firstName, lastName);
}
#Override
public String computeValue() {
String fullName = getFirstName() + " " + getLastName();
return scrambleName(fullName);
}
});
}
private String scrambleName(String fullName) {
digest.update(fullName.getBytes());
StringBuilder result = new StringBuilder();
for (byte b : digest.digest()) {
result.append(String.format("%02x", b));
}
return result.toString();
}
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);
}
public String getDigestedName() {
return digestedName.get();
}
}
}
Right now I have dialog show up at the mouse position when an row was selected in the tableview.
I'm looking to have the dialog show up when I hover over each row, there seems to be a CSS :hover so I assume it can be caught in java code in some capacity.
You can create a custom table row factory which adds a listener to the hover property of the row and takes action when the hover status changes.
Here is some sample code which updates a label as the user hovers over table rows:
table.setRowFactory(tableView -> {
final TableRow<Person> row = new TableRow<>();
row.hoverProperty().addListener((observable) -> {
final Person person = row.getItem();
if (row.isHover() && person != null) {
label.setText("Address Book: "
+ person.getFirstName() + " "
+ person.getLastName()
);
} else {
label.setText("Address Book");
}
});
return row;
});
In the sample image the mouse pointer (not shown) is hovered over the row for Emma Jones, so the title header is modified to read "Address Book: Emma Jones"
Here is a complete sample adopted from the Oracle JavaFX TableView tutorial sample code:
import javafx.application.Application;
import javafx.beans.property.SimpleStringProperty;
import javafx.collections.*;
import javafx.geometry.Insets;
import javafx.scene.*;
import javafx.scene.control.*;
import javafx.scene.control.cell.PropertyValueFactory;
import javafx.scene.layout.VBox;
import javafx.scene.text.Font;
import javafx.stage.Stage;
public class TableViewSample extends Application {
private TableView<Person> table = new TableView<Person>();
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")
);
public static void main(String[] args) {
launch(args);
}
#Override
public void start(Stage stage) {
final Label label = new Label("Address Book");
label.setFont(new Font("Arial", 20));
table.setEditable(true);
TableColumn<Person, String> firstNameCol = new TableColumn<>("First Name");
firstNameCol.setMinWidth(100);
firstNameCol.setCellValueFactory(
new PropertyValueFactory<>("firstName"));
TableColumn<Person, String> lastNameCol = new TableColumn<>("Last Name");
lastNameCol.setMinWidth(100);
lastNameCol.setCellValueFactory(
new PropertyValueFactory<>("lastName"));
TableColumn<Person, String> emailCol = new TableColumn<>("Email");
emailCol.setMinWidth(200);
emailCol.setCellValueFactory(
new PropertyValueFactory<>("email"));
table.setRowFactory(tableView -> {
final TableRow<Person> row = new TableRow<>();
row.hoverProperty().addListener((observable) -> {
final Person person = row.getItem();
if (row.isHover() && person != null) {
label.setText(
"Address Book: "
+ person.getFirstName() + " "
+ person.getLastName()
);
} else {
label.setText("Address Book");
}
});
return row;
});
table.setItems(data);
table.getColumns().addAll(firstNameCol, lastNameCol, emailCol);
final VBox vbox = new VBox(10);
vbox.setPadding(new Insets(10));
vbox.getChildren().addAll(label, table);
Scene scene = new Scene(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);
}
}
}