Does PropertyValueFactory need valueProperty method in model or not? - java

I am trying to make a table with a TableView and fill it based on a list of Actors object. The Actor Model is bellow.
public class Actor {
private SimpleIntegerProperty actorId;
private SimpleStringProperty firstName;
private SimpleStringProperty lastName;
private SimpleStringProperty email;
public Actor(int id, String first, String last, String e){
actorId = new SimpleIntegerProperty(id);
firstName = new SimpleStringProperty(first);
lastName = new SimpleStringProperty(last);
email = new SimpleStringProperty(e);
}
public void setActorId(int id){
actorId.set(id);
}
public int getActorId(){
return actorId.get();
}
public void setFirstName(String name){
firstName.set(name);
}
public String getFirstName(){
return firstName.get();
}
public void setLastName(String last){
lastName.set(last);
}
public String getLastName(){
return lastName.get();
}
public void setEmail(String e){
email.set(e);
}
public String getEmail(){
return email.get();
}
}
And here is my TableVeiw class
public class SakilaApp extends Application {
private TableView<Actor> actorTable = new TableView<Actor>();
private final ObservableList<Actor> actorData = FXCollections.observableArrayList(
new Actor(1, "Mohsen","Parsa", "Mohseh.parsa313#gmail.com"),
new Actor(2, "Morteza","Ghasemi", "Morteza.Ghasemi#gmail.com"),
new Actor(3, "Mohammad","Fetrat", "Mohammad.Fetrat#gmail.com"),
new Actor(4, "Nader","AhmadYar", "Nader.AhmadYar#gmail.com" )
);
#SuppressWarnings("unchecked")
#Override
public void start(Stage stage) {
Scene scene = new Scene(new Group());
stage.setTitle("Table View Sample");
stage.setWidth(600);
stage.setHeight(500);
final Label label = new Label("Address Book");
label.setFont(new Font("Arial", 20));
actorTable.setEditable(true);
TableColumn<Actor, Integer> idCol = new TableColumn<Actor, Integer>("Actor ID");
idCol.setCellValueFactory(
new PropertyValueFactory<Actor, Integer>("actorId"));
idCol.setPrefWidth(60);
TableColumn<Actor, String> firstNameCol = new TableColumn<Actor, String>("First Name");
firstNameCol.setCellValueFactory(
new PropertyValueFactory<Actor, String>("firstName"));
firstNameCol.setPrefWidth(100);
TableColumn<Actor, String> lastNameCol = new TableColumn<Actor, String>("Last Name");
lastNameCol.setCellValueFactory(
new PropertyValueFactory<Actor, String>("lastName"));
lastNameCol.setPrefWidth(100);
TableColumn<Actor, String> emailCol = new TableColumn<Actor, String>("Email");
emailCol.setCellValueFactory(
new PropertyValueFactory<Actor, String>("email"));
emailCol.setPrefWidth(200);
TableColumn<Actor, String> lastUpdateCol = new TableColumn<Actor, String>("Last Update");
lastUpdateCol.setCellValueFactory(
new PropertyValueFactory<Actor, String>("lastUpdate"));
lastUpdateCol.setPrefWidth(100);
actorTable.getColumns().addAll(idCol, firstNameCol, lastNameCol, emailCol);
actorTable.setItems(actorData);
final VBox actorBox = new VBox();
actorBox.setSpacing(5);
actorBox.setPadding(new Insets(10, 0, 0, 10));
actorBox.getChildren().addAll(label, actorTable);
((Group) scene.getRoot()).getChildren().addAll(actorBox);
stage.setScene(scene);
stage.show();
}
public static void main(String args[]){
Application.launch(args);
}
}
My problem is that as they mentioned in this article
How to use the PropertyValueFactory correctly?
new PropertyValueFactory<Actor, Integer>("actorId")
will lookup for :
Actor.actorIdProperty()
but as you can see in Actor model there is no any methods with the name of
IntegerProperty actorIdProperty()
My question is, Do we need such method or not?
if it is necessary, why this code works correctly?

Depends on what you mean by "work" :-)
As long as the TableView is read-only, getters/setters are enough: data is shown as expected. As soon as the TableView is editable, the data won't be updated automatically. In the latter case, you'll have the option to either install a custom commit handler or expose the properties which will allow internal magic to work.
As you have them anyway, I see no reason not to (and not follow buggy example in the tutorial)

The JavaDoc of PropertyValueFactory states that you need a field called
SimpleIntegerProperty actorIdProperty;
so really I think you should.
However, looking at the code of PropertyValueFactory I notice that it falls back to a getter if the property field isn't available:
if (propertyRef.hasProperty()) {
return propertyRef.getProperty(rowData);
} else {
T value = propertyRef.get(rowData);
return new ReadOnlyObjectWrapper<T>(value);
}
so that is why your code works as is.
If I were you I would follow the JavaDoc and rename your field to actorIdProperty because you never know when they could change the implementation.

Related

Check if TextFieldTableCell equals something on edit commit?

I am using a custom TextFieldTableCell in JavaFX 8 to allow users to edit the text field. When the user hits Enter, however, I want to check to see if the text field equals a certain value. If it does equal this certain value, I do not want the entry to save and for it to revert to the text it had before the user started editing. Is there a method I can override to produce this result? I cannot find one that fits what I am looking for.
Thank you in advance!
Since the model is bound to the cell's data, you can do the validation and reset part in the modell class, in your case in the Person class.
Here is a simple example how you can do it:
public class Controller implements Initializable {
#FXML
private TableColumn<Model, String> name;
#FXML
private TableView<Model> tableView;
#Override
public void initialize(URL location, ResourceBundle resources) {
name.setCellValueFactory(data -> data.getValue().nameProperty());
name.setCellFactory(cell -> new TextFieldTableCell<>(new StringConverter<String>() {
#Override
public String toString(String object) {
return object;
}
#Override
public String fromString(String string) {
return string;
}
}));
tableView.setEditable(true);
tableView.setItems(FXCollections.observableArrayList(new Model("Test")));
}
private class Model {
private StringProperty name;
ChangeListener<String> nameChangeListener = (observable, oldValue, newValue) -> {
if (!newValue.matches("[A-Z][a-zA-Z]*")) { // a validation example insert your here.
this.name.set(oldValue);
}
};
public Model(String name) {
this.name = new SimpleStringProperty(name);
this.name.addListener(nameChangeListener);
}
public String getName() {
return name.get();
}
public StringProperty nameProperty() {
return name;
}
}
}
From your description I assume that you are talking about an editable cell in a table view.
If that's the case, the following example should be working for you. You can use the .setOnEditCommit() method to add an event handler to the colum for which you would like to check the entered value.
//Create table
TableView<Person> table = new TableView<Person>();
table.setEditable(true);
//Create column
TableColumn<Person, String> column = new TableColumn<Person, String>("Full Name");
column.setCellValueFactory(new PropertyValueFactory<>("fullName"));
column.setCellFactory(TextFieldTableCell.<Person> forTableColumn());
column.setMinWidth(200);
column.setOnEditCommit(event -> {
//Get entered value
String newFullName = event.getNewValue();
//Get selected position
TablePosition<Person, String> pos = event.getTablePosition();
//Get row of position
int row = pos.getRow();
//Get data from selected row
Person person = event.getTableView().getItems().get(row);
//Check if text equals ...
if (newFullName.equals("Test")) {
person.setFullName(newFullName);
} else {
person.setFullName(event.getOldValue());
table.refresh();
}
});

Is there anyway to generate UI controllers (radio button, input fields, etc) using a loop in java

I'm a beginner in java. I want to create a javafx form which contains a few input elements such as radio buttons, input types, etc.. created using a loop.
A pseudo code as follows,
for (Suit **suit** : suits){
final TextField **suit** = new TextField();
lastName.setPromptText("Enter your last name.");
GridPane.setConstraints(lastName, 0, 1);
grid.getChildren().add(lastName);
}
The problem i came across is how to define the variable name of each element based on elements of the array.
The easiest way to do this is, if the class you want to edit contains javafx properties:
public class Person {
private final StringProperty firstName;
private final StringProperty familyName;
public String getFirstName() {
return firstName.get();
}
public String getFamilyName() {
return familyName.get();
}
public StringProperty firstNameProperty() {
return firstName;
}
public StringProperty familyNameProperty() {
return familyName;
}
public void setFirstName(String firstName) {
this.firstName.set(firstName);
}
public void setFamilyName(String familyName) {
this.familyName.set(familyName);
}
public Person(String firstName, String familyName) {
this.firstName = new SimpleStringProperty(firstName);
this.familyName = new SimpleStringProperty(familyName);
}
#Override
public String toString() {
return familyName.get() + ", " + firstName.get();
}
}
You could simply use binding/listeners in this case to bind the properties of the ui to the properties of the person object:
Example:
Person[] persons = new Person[] {
new Person("Frank", "Miller"),
new Person("Jane", "Doe"),
new Person("Luke", "Skywalker"),
new Person("Leia", "Organa")
};
Button btn = new Button();
btn.setText("Print");
btn.setOnAction((ActionEvent event) -> {
System.out.println("-------------------------");
Arrays.stream(persons).forEach(System.out::println);
});
GridPane root = new GridPane();
int row = 0;
for (Person person : persons) {
row = addPerson(root, row, person);
}
GridPane.setConstraints(btn, 0, row);
root.getChildren().add(btn);
/**
* Adds a Person to a GridPane
* #param pane the pane to add to.
* #param startRow the first row used by this person
* #param person the person to add.
* #return first row below the Nodes added for the person.
*/
static int addPerson(GridPane pane, int startRow, Person person) {
// use binding
TextField firstNameText = new TextField();
firstNameText.textProperty().bindBidirectional(person.firstNameProperty());
// use listeners
TextField familyNameText = new TextField(person.getFamilyName());
familyNameText.textProperty().addListener((ObservableValue<? extends String> observable, String oldValue, String newValue) -> {
// you could do e.g. type conversion here
person.setFamilyName(newValue);
});
// this is only necessary, if you want to change the family name of a person
// from the code and the result should be visible in the ui
person.familyNameProperty().addListener((ObservableValue<? extends String> observable, String oldValue, String newValue) -> {
// you could do e.g. type conversion here
familyNameText.setText(newValue);
});
Label firstNameLabel = new Label("First Name");
GridPane.setConstraints(firstNameLabel, 0, startRow);
GridPane.setConstraints(firstNameText, 1, startRow);
Label familyNameLabel = new Label("Family Name");
GridPane.setConstraints(familyNameLabel, 2, startRow);
GridPane.setConstraints(familyNameText, 3, startRow);
pane.getChildren().addAll(firstNameLabel, firstNameText, familyNameLabel, familyNameText);
return startRow+1;
}
Note that a GridPane may not be the best way to add a list of persons to a scene your problem.
See Oracle Tutorial: JavaFX: Working with JavaFX UI Components: 13 Table View for an example using a TableView.

JavaFX: TableView row selection

I am working on a java/Javafx project for the first time and i have a TableView with multiple column (name, prename, age...) to present my data and I need the user to be able to select a single row and give me everytime all anformation about the person(Other columns) even when he click at another column but I haven't been able to find the right way to do it.
When i select a row my code give everytime the value of the cell i click on, but i need other informations to search with in my SQLite data base and work on it (Delete/edit this person..)
Here is the code that i use:
...//rest of code
#Override
public void initialize(URL location, ResourceBundle resources) {
private TableView<Student> tbl_elev=new TableView<Student>();
...
tbl_elev.getSelectionModel().selectedItemProperty().addListener(new ChangeListener<Object>() {
#Override
public void changed(ObservableValue<?> observableValue, Object oldValue, Object newValue) {
//Check whether item is selected and set value of selected item to Label
if (tbl_elev.getSelectionModel().getSelectedItem() != null) {
TableViewSelectionModel<Student> selectionModel = tbl_elev.getSelectionModel();
ObservableList<?> selectedCells = selectionModel.getSelectedCells();
#SuppressWarnings("unchecked")
TablePosition<Object, ?> tablePosition = (TablePosition<Object, ?>) selectedCells.get(0);
Object val = tablePosition.getTableColumn().getCellData(newValue);
System.out.println("Selected Value " + val);
}
}
});
}
... //rest of code
I am waiting for your suggestions and ideas, i dont mind if you suggest another approach because this may be uncompatible (taken from internet) Please if you need any other part of the code just comment, i don't put it all because it is too long to read.. (Sorry of my bad english)
If you specify that the ChangeListener parameters are of type Student you can get use the instance methods from that object:
Here's a Minimal, Complete, and Verifiable example:
import javafx.application.Application;
import javafx.beans.property.SimpleStringProperty;
import javafx.beans.property.StringProperty;
import javafx.beans.value.ChangeListener;
import javafx.beans.value.ObservableValue;
import javafx.collections.ObservableList;
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.HBox;
import javafx.scene.layout.VBox;
import javafx.stage.Stage;
public class SSCCE extends Application {
#Override
public void start(Stage stage) {
VBox root = new VBox();
TableView<Student> studentsTable = new TableView<Student>();
HBox studentBox = new HBox();
Label studentHeader = new Label("Student: ");
Label studentInfo = new Label("");
studentBox.getChildren().addAll(studentHeader, studentInfo);
root.getChildren().addAll(studentsTable, studentBox);
// Prepare the columns
TableColumn<Student, String> firstNameCol = new TableColumn<Student, String>(
"First name");
firstNameCol.setCellValueFactory(cellData -> cellData.getValue()
.firstNameProperty());
TableColumn<Student, String> lastNameCol = new TableColumn<Student, String>(
"Last name");
lastNameCol.setCellValueFactory(cellData -> cellData.getValue()
.lastNameProperty());
studentsTable.getSelectionModel().selectedItemProperty()
.addListener(new ChangeListener<Student>() {
// Here's the key part. See how I specify that the
// parameters are of type student. Now you can use the
// instance methods from Student.
#Override
public void changed(
ObservableValue<? extends Student> observable,
Student oldValue, Student newValue ) {
studentInfo.setText(newValue.getFirstName() + " "
+ newValue.getLastName());
// If you want to get the value of a selected student cell at
// anytime, even if it hasn't changed. Just do e.g.
// studentsTable.getSelectionModel().getSelectedItem().getFirstName()
}
});
studentsTable.getColumns().setAll(firstNameCol, lastNameCol);
// Some mock Student objects
Student student1 = new Student("Eric", "Smith");
Student student2 = new Student("Brad", "Jones");
Student student3 = new Student("Logan", "Thorpe");
// Fill the table with students.
studentsTable.getItems().addAll(student1, student2, student3);
Scene scene = new Scene(root);
stage.setScene(scene);
stage.show();
}
public static void main(String[] args) {
launch();
}
// The student class. In this case an inner class to simplify the example. But generally you should never use inner classes.
class Student {
private StringProperty firstName;
private StringProperty lastName;
public Student(String firstName, String lastName) {
this.firstName = new SimpleStringProperty(firstName);
this.lastName = new SimpleStringProperty(lastName);
}
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;
}
}
}
After too many failed attempts and thanks to #Jonatan 's answer the code after i compelete some missing words should be like this:
...//rest of code
#Override
public void initialize(URL location, ResourceBundle resources) {
private TableView<Student> tbl_elev=new TableView<Student>();
...
tbl_elev.getSelectionModel().selectedItemProperty().addListener(new ChangeListener<Student>() {
// Here's the key part. See how I specify that the
// parameters are of type student. Now you can use the
// instance methods from Student.
#Override
public void changed(ObservableValue<? extends Student> observable,Student oldValue, Student newValue){
if(newValue!=null){
System.out.println(newValue.getName() + " "+ newValue.getPrename()+" "+newValue.getNaiss());
}
//you can add any other value from Student class via getter(getAdr,getMail,...)
}
});
}
... //rest of code
Output example:
Jonatan stenbacka 2015-09-11
Those value are ready for use to fetch the data base and specify the needed row in it to work on.
Hope that this help someone one day.
thanks...

How to build TreeView from Arraylist

I'm looking for a solution how to build a JavaFX TreeView from an ArrayList. I have this ArrayList witch contains connection name, database server name and list of tables:
public List<ConnectionsListObj> connListObj = new ArrayList<>();
public class ConnectionsListObj {
private String connectionName;
private String dbgwName;
private String tableName;
public ConnectionsListObj(String connectionName, String dbgwName, String tableName) {
this.connectionName = connectionName;
this.dbgwName = dbgwName;
this.tableName = tableName;
}
public String getConnectionName() {
return connectionName;
}
public void setConnectionName(String connectionName) {
this.connectionName = connectionName;
}
public String getDbgwName() {
return dbgwName;
}
public void setDbgwName(String dbgwName) {
this.dbgwName = dbgwName;
}
public String getTableName() {
return tableName;
}
public void setTableName(String tableName) {
this.tableName = tableName;
}
}
I need some kind of a loop which looks into the tree and generates tree using this code:
TreeItem<String> treeItemConnections = new TreeItem<> ("Connections");
TreeItem<String> nodeItemDBGW = new TreeItem<>("DBGW 1");
treeItemConnections.getChildren().add(nodeItemDBGW);
TreeItem<String> nodeItemTable = new TreeItem<>("Table 1");
nodeItemDBGW.getChildren().add(nodeItemTable);
TreeView<String> treeView = new TreeView<>(treeItemConnections);
StackPane root = new StackPane();
root.getChildren().add(treeView);
Scene scene = new Scene(root, 300, 250);
primaryStage.setTitle("java-buddy.blogspot.com");
primaryStage.setScene(scene);
primaryStage.show();
The question is how I can make a loop which looks into the ArrayList and constructs the three? And also when I select on a node I want to get the type of the node.
Why not just put the ConnectionsListObj objects in the tree? I think TreeView calls toString() on the objects for the text in each tree node so just return the string you want to show from ConnectionsListObj.toString(). Then when you get the selected item by calling myTreeView.getSelectionModel().getSelectedItems() you get an instance of ConnectionsListObj which should have all the data you need.
Loops in java look like the following for your case:
for(ConnectionsListObj connection : connListObj) {
nodeItemDBGW.getChildren().add(connection);
}
or...
nodeItemDBGW.getChildren().addAll(connListObj);

JavaFX 2.0 TableViewBuilder, how to use

Given a Person class:
public class Person {
private StringProperty firstName;
private StringProperty lastName;
public Person(String firstName, String lastName){
setFirstName(firstName);
setLastName(lastName);
}
//SETTERS
public final void setFirstName(String value) { firstNameProperty().set(value); }
public final void setLastName(String value) { lastNameProperty().set(value); }
//GETTERS
public String getFirstName() { return firstNameProperty().get(); }
public String getLastName() { return lastNameProperty().get(); }
//PROPERTY GETTERS
public StringProperty firstNameProperty() {
if (firstName == null) firstName = new SimpleStringProperty(this, "firstName");
return firstName;
}
public StringProperty lastNameProperty() {
if (lastName == null) lastName = new SimpleStringProperty(this, "lastName");
return lastName;
}
}
I recreated the JavaFX API example on TableView:
public class TestTableViewBuilder extends Application {
public static void main(String[] args) {
launch(args);
}
#Override
public void start(Stage primaryStage) {
primaryStage.setTitle("Hello World!");
final ObservableList<Person> data = FXCollections.observableArrayList(
new Person("Jacob", "Smith"),
new Person("Isabella", "Johnson"),
new Person("Ethan", "Williams"),
new Person("Emma", "Jones"),
new Person("Michael", "Brown")
);
TableView<Person> table = new TableView<Person>();
table.setItems(data);
TableColumn<Person,String> firstNameCol = new TableColumn<Person,String>("First Name");
firstNameCol.setCellValueFactory(new PropertyValueFactory("firstName"));
TableColumn<Person,String> lastNameCol = new TableColumn<Person,String>("Last Name");
lastNameCol.setCellValueFactory(new PropertyValueFactory("lastName"));
table.getColumns().setAll(firstNameCol, lastNameCol);
Group root = new Group();
root.getChildren().add(table);
primaryStage.setScene(new Scene(root));
primaryStage.show();
}
}
I've been trying without success to use the TableViewBuilder to recreate the same table. Anyone have an idea how to use JavaFX 2.0 TableViewBuilder to create a TableView with an existing ObservableList?
Here is a sample:
import javafx.application.Application;
import javafx.collections.*;
import javafx.scene.Scene;
import javafx.scene.control.*;
import javafx.scene.control.cell.PropertyValueFactory;
import javafx.stage.Stage;
public class TableViewBuilderExample extends Application {
public static void main(String[] args) { launch(args); }
#Override public void start(Stage stage) {
final ObservableList<?> data = FXCollections.observableArrayList(
new Person("Jacob", "Smith"),
new Person("Isabella", "Johnson")
);
stage.setScene(
new Scene(
TableViewBuilder.create().items((ObservableList<Object>) data).columns(
TableColumnBuilder.create().text("First Name").cellValueFactory(new PropertyValueFactory("firstName")).build(),
TableColumnBuilder.create().text("Last Name").cellValueFactory(new PropertyValueFactory("lastName")).build()
).build()
)
);
stage.show();
}
}
There are some strange things going on with the generic type usage in the Builders. I would have liked instead to say something like TableViewBuilder<Person>.create(), but TableViewBuilder has a recursive type as a second generic type parameter which must be supplied to it, so I could not get that strategy to work. The code above is next best thing I could come up with, but it still have some strange typing going on with the ObservableList<?> definition of the data and the need to cast the data to an ObservableList<Object> in the Builder.
Based on Sergey's insight for a type parameterization syntax for the builders I was able to create the following builder which will work with a data type of ObservableList<Person>
TableViewBuilder.<Person>create().items(data).columns(
TableColumnBuilder.<Person, String>create()
.text("First Name").cellValueFactory(new PropertyValueFactory("firstName"))
.build(),
TableColumnBuilder.<Person, String>create()
.text("Last Name").cellValueFactory(new PropertyValueFactory("lastName"))
.build()
).build()
After this exercise, I would be even more inclined to checkout the DataFX project if I had to do this kind of stuff a lot . . .
The trick here is in the fact that Builders are created by factories named create, so you have to parametrize them, not the Builder class name itself which only plays namespace role here.
This way:
TableViewBuilder.<Person>create().build();

Categories

Resources