Populate control without flooding FXThread - java

I have a following situation:
Large file with a lot of lines (~100k, logs from server). Each line in this file should be parsed, filtered and disaplayed on UI.
To read data from file I use BufferedReader, wich read lines, parse it and prepare for disaplying. It runs on different thread (THREAD-1) and populates BlockingQueue. In another thread (THREAD-2) runned UIUpdater - it purpose to get line batch from queue and run something line this:
Platform.runLater(() -> logArea.append(batchedLine));
Obviously, FX Thread floods and UI is freezes.
So, question is: where I can get information about patterns/best practices to resolove this issue?

It really depends on the control that you want to populate.
Adding lots of nodes to the scene-graph is expensive therefore it will be slow (for example putting Text objects to any container).
I would suggest the usage of a control that was originally designed to display a huge amount of data, like ListView.
In the example, even during the update of the ListView the Button is responsive.
Main.java
public class Main extends Application {
#Override
public void start(Stage primaryStage) {
HBox root = new HBox();
Scene scene = new Scene(root, 700, 400, Color.WHITE);
TableView<Person> personsTable = new TableView<Person>();
TableColumn<Person, String> nameCol = new TableColumn<Person, String>("Name");
nameCol.setCellValueFactory(new PropertyValueFactory<>("name"));
personsTable.getColumns().add(nameCol);
ObservableList<Person> persons = FXCollections.observableArrayList();
Thread th = new Thread(new Runnable() {
#Override
public void run() {
for (int i = 0; i < 100000; i++) {
Person person = new Person();
person.setName("Name" + i);
person.setAddress("Address" + i);
person.setCountry("Country" + i);
person.setCourse("Course" + i);
persons.add(person);
try {
Thread.sleep(5);
} catch (InterruptedException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}
}
}) ;
th.start();
personsTable.setItems(persons);
Button b = new Button();
b.setOnAction(new EventHandler<ActionEvent>() {
#Override
public void handle(ActionEvent event) {
System.out.println("I am printing independently of Person update!");
}
});
root.getChildren().addAll(personsTable, b);
primaryStage.setScene(scene);
primaryStage.show();
}
public static void main(String[] args) {
launch(args);
}
}
Person.java
public class Person {
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public String getAddress() {
return address;
}
public void setAddress(String address) {
this.address = address;
}
public String getCountry() {
return country;
}
public void setCountry(String country) {
this.country = country;
}
public String getCourse() {
return course;
}
public void setCourse(String course) {
this.course = course;
}
private String name;
private String address;
private String country;
private String course;
}
User jewelsea has made a really good example on lgging. With little tailoring it could solve your issue.

Related

How to bind label.textProperty() to tableView.selectedItem() in JavaFX

I have some problem with understanding bindings in JavaFX.
For example I use simple class Person for showing data in TableView.
public class Person {
private StringProperty name=new SimpleStringProperty();
public Person() {
}
public Person(StringProperty name) {
this.name = name;
}
public String getName() {
return name.get();
}
public StringProperty nameProperty() {
return name;
}
public void setName(String name) {
this.name.set(name);
}
}
I have PersonDataModel class too
public class PersonDataModel {
private ObservableList<Person> personObservableList= FXCollections.observableArrayList();
private SimpleObjectProperty<Person> singlePerson=new SimpleObjectProperty<>(new Person());
public void listInitialize(){
personObservableList.clear();
personObservableList.add(new Person(new SimpleStringProperty("John")));
personObservableList.add(new Person(new SimpleStringProperty("Mike")));
personObservableList.add(new Person(new SimpleStringProperty("Ana")));
}
public ObservableList<Person> getPersonObservableList() {
return personObservableList;
}
public void setPersonObservableList(ObservableList<Person> personObservableList) {
this.personObservableList = personObservableList;
}
public Person getSinglePerson() {
return singlePerson.get();
}
public SimpleObjectProperty<Person> singlePersonProperty() {
return singlePerson;
}
public void setSinglePerson(Person singlePerson) {
this.singlePerson.set(singlePerson);
}
}
And now I want bind label.textProperty() with selected item in tableView.I use controller class.
public class PersonWindowController {
private PersonDataModel personDataModel=new PersonDataModel();
#FXML
private TableView<Person> nameTableView;
#FXML
private TableColumn<Person, String> nameTableColumn;
#FXML
private Label nameLabel;
#FXML
public void initialize(){
personDataModel.listInitialize();
nameTableColumn.setCellValueFactory(cellData->cellData.getValue().nameProperty());
nameTableView.setItems(personDataModel.getPersonObservableList());
nameLabel.textProperty().bind(personDataModel.getSinglePerson().nameProperty());
nameTableView.getSelectionModel().selectedItemProperty().addListener((observable, oldValue, newValue) -> {
if(newValue!=null) {
//1. Option doesn't work
//personDataModel.setSinglePerson(newValue);
//System.out.println(personDataModel.getSinglePerson().getName());
//2. Option works
personDataModel.getSinglePerson().setName(newValue.getName());
System.out.println(personDataModel.getSinglePerson().getName());
}
});
}
}
My questions are:
Why first option doesn't work ?. I changed singlePerson object but nameLabel didn't change.
I don't want to set all properties one by one when Person class will be more complicated. How to avoid this ?.
When singlePerson in PersonDataModel is just an object of Person class not SimpleObjectProperty the programm behaves exactly the same. Is any advantage to use SimpleObjectProperty in my example ?
Sorry for all mistakes I've made. Thanks in advance for your help.
Greg
Edit1: I edited my question after user Kleopatra answer.

JavaFX - MVC Application best practices with database

I'm new to JavaFX and I was wondering what are the best practices in this language to develop a MVC database application, I think my question will be pretty simple if you are an senior developer.
Let us consider a simple example of a basic application developed in JavaFX : a ToDoList linked with a SQL Database.
The database is just one table Task with an id and a taskDescr VARCHAR field.
The purpose is pretty easy : we just want to display the task in a TableView or ListView and be able to add some tasks.
That's what our application looks like :
ToDoList GUI
I decided to split my code into four parts, DAO for the classes who represents datas in the table (Task.java), the DAO class who access the database (its behavior does not matter here). The model who represents the Model part of our TodoList (containing a list of task and performing operations on it, calling the DAO, etc..). The FXML Views and the Controller :
Project structure
Next, you can find the code of the different classes that interest us (We supposed that the DAO is OK (setting id automatically) an we do not handle error cases to simplify code :
Task.java
public class Task {
private int id;
private SimpleStringProperty task;
public Task(int i, String s){
this.id = i;
this.task = new SimpleStringProperty(s);
}
public void setId(int i){
this.id = i;
}
public int getId() {
return id;
}
public String getTask() {
return task.get();
}
public void setTask(String task) {
this.task.set(task);
}
#Override
public boolean equals(Object o){
if(this.id == ((Task)o).id)
return true;
return false;
}
}
ToDoListModel.java
public class ToDoListModel {
private List<Task> taskList;
private DAO dao;
public ToDoListModel(){
this.taskList = new ArrayList<Task>();
this.dao = new DAO();
}
public void loadDatabase(){
this.taskList = this.dao.getAllTasks();
}
public void addTask(Task t){
// Operations throwing Exceptions such as : Does the task t is already in the list, etc...
this.taskList.add(t);
this.dao.createTask(t);
}
public void deleteTask(Task t){
this.taskList.remove(t);
this.dao.deleteTask(t);
}
public List<Task> getTaskList() {
return taskList;
}
}
Controller.java
public class Controller {
private final ToDoListModel model;
#FXML
private TableView<Task> taskTable;
#FXML
private TableColumn<Task, String> taskColumn;
#FXML
private TextField taskTextField;
public Controller(ToDoListModel m){
this.model = m;
}
#FXML
protected void initialize() {
this.model.loadDatabase();
// Setting up data table
taskColumn.setCellValueFactory(new PropertyValueFactory<Task, String>("task"));
ObservableList<Task> taskObservableList = FXCollections.observableList(this.model.getTaskList());
taskTable.setItems(taskObservableList);
}
#FXML
public void handleAddButton(ActionEvent e) {
Task t = new Task(-1, this.taskTextField.getText());
// What operations to do here ?
this.model.addTask(t);
this.taskTable.getItems().add(t);
this.taskTable.refresh();
}
}
Main.java
public class Main extends Application {
#Override
public void start(Stage primaryStage) throws Exception{
ToDoListModel model = new ToDoListModel();
primaryStage.setTitle("My Todo");
FXMLLoader loader = new FXMLLoader();
loader.setLocation(getClass().getResource("views/View.fxml"));
loader.setController(new Controller(model));
Parent root = loader.load();
primaryStage.setScene(new Scene(root));
primaryStage.show();
}
public static void main(String[] args) {
launch(args);
}
}
Finally, my question is : Is my approach good ? I mean the fact that I've created a ToDoListModel with a list of task, the fact that I update my list of Objects Task at the same task I update my database with the DAO (a create in the DAO will be performed after an add in the list) and the most important : what operations should I do in the handleAddButton of my Controller ? Here I used first the add method in my TodoListModel but it's not enough because my observable list is wrongly updated (The added task appears but we can not select it with the mouse). Then, when I add it also in the TableView items, the Task appears twice and has been added twice in the list.
As a result I've understood that the ObservableList was linked to the List I have in my ToDoListModel but what am I supposed to do if I want to do operations on that list only in my model but getting the ObservableList updated correctly ? (Selectable item etc...)
Duplication example
Thank you in advance for your help and your patience,
Sincerely,
Paul
Here is an example implementation
The DAO class takes care of connecting to the database (may use a pool or something else). In this case, it makes a simple connection.
public class DAO {
public Connection getConnection() throws SQLException {
return DriverManager.getConnection("jdbc:mysql://192.168.40.5:3306/test", "root", "");
}
}
The ToDoListModel class takes care of working with the database by using an instance of DAO to get a valid connection.
public class ToDoListModel {
private DAO dao;
public static ToDoListModel getInstance() {
ToDoListModel model = new ToDoListModel();
model.dao = new DAO();
return model;
}
private ToDoListModel() {
}
public void addTask(Task task) throws SQLException {
try(Connection connection = dao.getConnection()) {
String q = "insert into todo (name) values (?)";
try(PreparedStatement statement = connection.prepareStatement(q, Statement.RETURN_GENERATED_KEYS)) {
statement.setString(1, task.getName());
statement.executeUpdate();
try(ResultSet rs = statement.getGeneratedKeys()) {
if(rs.next()) {
task.setId(rs.getInt(1));
}
}
}
}
}
public void deleteTask(Task task) throws SQLException {
try(Connection connection = dao.getConnection()) {
String q = "delete from todo where id = ?";
try(PreparedStatement statement = connection.prepareStatement(q)) {
statement.setInt(1, task.getId());
statement.executeUpdate();
}
}
}
public ObservableList<Task> getTaskList() throws SQLException {
try(Connection connection = dao.getConnection()) {
String q = "select * from todo";
try(Statement statement = connection.createStatement()) {
try(ResultSet rs = statement.executeQuery(q)) {
ObservableList<Task> tasks = FXCollections.observableArrayList();
while (rs.next()) {
Task task = new Task();
task.setId(rs.getInt("id"));
task.setName(rs.getString("name"));
tasks.add(task);
}
return tasks;
}
}
}
}
}
The controller uses ToDoListModel to initialize TableView controls and add operations (editing and reading - I did not implement them because I stick to your code)
public class Controller {
#FXML
private TextField textField;
#FXML
private TableView<Task> tableView;
#FXML
private TableColumn<Task, String> nameTableColumn;
#FXML
private Button addButton;
#FXML
private void initialize() {
nameTableColumn.setCellValueFactory(cdf -> cdf.getValue().nameProperty());
addButton.disableProperty().bind(Bindings.isEmpty(textField.textProperty()));
CompletableFuture.supplyAsync(this::loadAll)
.thenAccept(list -> Platform.runLater(() -> tableView.getItems().setAll(list)))
.exceptionally(this::errorHandle);
}
#FXML
private void handleAddButton(ActionEvent event) {
CompletableFuture.supplyAsync(this::addTask)
.thenAccept(task -> Platform.runLater(() -> {
tableView.getItems().add(task);
textField.clear();
textField.requestFocus();
}))
.exceptionally(this::errorHandle);
}
private Task addTask() {
try {
Task task = new Task(textField.getText());
ToDoListModel.getInstance().addTask(task);
return task;
}
catch (SQLException e) {
throw new RuntimeException(e);
}
}
private ObservableList<Task> loadAll() {
try {
return ToDoListModel.getInstance().getTaskList();
}
catch (SQLException e) {
throw new RuntimeException(e);
}
}
private Void errorHandle(Throwable throwable) {
throwable.printStackTrace();
return null;
}
}
Any database operations are asynchronous with CompletableFuture but you can use whatever you prefer. The important thing is to remember that UI threads can only be made uniquely by it.

JavaFx: can data binding (textfield) be used in production mode?

I use java 8.0.45. I have implemented my first javafx application (very simple) with data binding. However, biding from user input-> pojo seems to work with bugs. I've checked about 200 times. I entered new values in text fields and after that I checked model values. The same code, the same my behaviour. Sometimes everything works fine (in most cases - about 80-90%) sometimes model value!=textfield value. I've noticed the following. Data binding for some certain text field works,works and then at some point of time that binding stops working and all new values for this certain textfield are not passed to model. Nor exceptions. Nor any warnings. Nothing. Just binding doesn't work.
I have 4 textfiled which are created via fxml. Two for string model type. One for integer. One for bigdecimal. The problem happens to all these fields(sometimes to one, sometimes to several). As my number fields can have null values, I use for example PropertyObject but not IntegerProperty (people from openjfx advised so).
So is this JavaFx bug or what? P.S. I use felix osgi, weld cdi, and pax - I don't know if it matters...
My code is the following:
DTO - POJO Model
public class Task {
private String name;
private Integer order;
private BigDecimal weight;
private String comment;
private final PropertyChangeSupport propertyChangeSupport;
public Task() {
this.propertyChangeSupport = new PropertyChangeSupport(this);
}
public String getName() {
return name;
}
public void setName(String name) {
String pv = this.name ;
this.name = name;
propertyChangeSupport.firePropertyChange("name", pv, name);
}
public Integer getOrder() {
return order;
}
public void setOrder(Integer order) {
Integer pv = this.order;
this.order = order;
propertyChangeSupport.firePropertyChange("order", pv, this.order);
}
public BigDecimal getWeight() {
return weight;
}
public void setWeight(BigDecimal weight) {
BigDecimal pv = this.weight;
this.weight = weight;
propertyChangeSupport.firePropertyChange("weight", pv, weight);
}
public String getComment() {
return comment;
}
public void setComment(String comment) {
String pv = this.comment;
this.comment = comment;
propertyChangeSupport.firePropertyChange("comment", pv, this.comment);
}
public void addPropertyChangeListener(PropertyChangeListener listener) {
propertyChangeSupport.addPropertyChangeListener(listener);
}
}
Adapter
public class TaskAdapter {
private StringProperty nameProperty;
private ObjectProperty<Integer> orderProperty;
private ObjectProperty<BigDecimal> weightProperty;
private StringProperty commentProperty;
public TaskAdapter(Task task) {
try {
nameProperty=new JavaBeanStringPropertyBuilder().bean(task).name("name").build();
orderProperty=new JavaBeanObjectPropertyBuilder<Integer>().bean(task).name("order").build();
weightProperty=new JavaBeanObjectPropertyBuilder<BigDecimal>().bean(task).name("weight").build();
commentProperty=new JavaBeanStringPropertyBuilder().bean(task).name("comment").build();
} catch (NoSuchMethodException ex) {
Logger.getLogger(this.getClass().getName()).log(Level.SEVERE, null, ex);
}
}
public StringProperty getNameProperty() {
return nameProperty;
}
public ObjectProperty<Integer> getOrderProperty() {
return orderProperty;
}
public ObjectProperty<BigDecimal> getWeightProperty() {
return weightProperty;
}
public StringProperty getCommentProperty() {
return commentProperty;
}
}
BigDecimal Converter
public class SimpleBigDecimalStringConverter extends StringConverter<BigDecimal>{
#Override
public String toString(BigDecimal i) {
if (i == null) {
return "" ;
} else {
return i.toString();
}
}
#Override
public BigDecimal fromString(String string) {
if (string.trim().length() == 0) {
return null ;
} else {
try {
return new BigDecimal(string);
} catch (NumberFormatException nfe) {
return null ;
}
}
}
}
IntegerConverter
public class SimpleIntegerStringConverter extends StringConverter<Integer>{
#Override
public String toString(Integer i) {
if (i == null) {
return "" ;
} else {
return i.toString();
}
}
#Override
public Integer fromString(String string) {
if (string.trim().length() == 0) {
return null ;
} else {
try {
return Integer.valueOf(string);
} catch (NumberFormatException nfe) {
return null ;
}
}
}
}
Initializing code
Task task=new Task();
TaskAdapter adapter=new TaskAdapter(task);
nameTextField.textProperty().bindBidirectional(adapter.getNameProperty());
orderTextField.textProperty().bindBidirectional(adapter.getOrderProperty(),new SimpleIntegerStringConverter());
weightTextField.textProperty().bindBidirectional(adapter.getWeightProperty(),new BigDecimalStringConverter());
commentTextField.textProperty().bindBidirectional(adapter.getCommentProperty());
What is happening
JavaFX Bindings use WeakChangeListeners behind the scenes to implement the binding. This means that the binding itself can be garbage collected if no other references to it are in scope. In your code, the adapter is defined as a local variable, so it gets prematurely garbage collected at some arbitrary time when the gc runs.
Demo
Here's a demo using your code that shows the issue. It has the same text fields you define, plus two buttons. One button dumps the value of the task to the console, the other forces the garbage collector to run. You'll see that the binding stops working as soon as you run the gc.
import javafx.application.Application;
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.TextField;
import javafx.scene.layout.BorderPane;
import javafx.scene.layout.GridPane;
import javafx.scene.layout.HBox;
import javafx.stage.Stage;
import javafx.util.converter.BigDecimalStringConverter;
public class POJOBindingExample extends Application {
private TextField nameTextField = new TextField();
private TextField orderTextField = new TextField();
private TextField weightTextField = new TextField();
private TextField commentTextField = new TextField();
#Override
public void start(Stage primaryStage) {
Task task = new Task();
TaskAdapter adapter = new TaskAdapter(task);
nameTextField.textProperty().bindBidirectional(adapter.getNameProperty());
orderTextField.textProperty().bindBidirectional(adapter.getOrderProperty(),new SimpleIntegerStringConverter());
weightTextField.textProperty().bindBidirectional(adapter.getWeightProperty(),new BigDecimalStringConverter());
commentTextField.textProperty().bindBidirectional(adapter.getCommentProperty());
GridPane grid = new GridPane();
grid.addRow(0, new Label("Name:"), nameTextField);
grid.addRow(1, new Label("Order:"), orderTextField);
grid.addRow(2, new Label("Weight:"), weightTextField);
grid.addRow(3, new Label("Comment:"), commentTextField);
Button showButton = new Button("Show Task");
showButton.setOnAction(e -> {
System.out.println(task.getName());
System.out.println(task.getOrder());
System.out.println(task.getWeight());
System.out.println(task.getComment());
System.out.println();
});
Button gcButton = new Button("Run GC");
gcButton.setOnAction(e -> System.gc());
HBox buttons = new HBox(10, showButton, gcButton);
BorderPane.setAlignment(grid, Pos.CENTER);
BorderPane.setAlignment(buttons, Pos.CENTER);
BorderPane.setMargin(grid, new Insets(10));
BorderPane.setMargin(buttons, new Insets(10));
BorderPane root = new BorderPane(grid, null, null, buttons, null);
Scene scene = new Scene(root);
primaryStage.setScene(scene);
primaryStage.show();
}
public static void main(String[] args) {
launch(args);
}
}
Fix
To fix the problem, you need to ensure a reference to the TaskAdapter persists as long as you need it. In the above code, if you move the reference to the TaskAdapter so that it is an instance field, everything will work as required:
public class POJOBindingExample extends Application {
private TextField nameTextField = new TextField();
private TextField orderTextField = new TextField();
private TextField weightTextField = new TextField();
private TextField commentTextField = new TextField();
private TaskAdapter adapter;
#Override
public void start(Stage primaryStage) {
Task task = new Task();
adapter = new TaskAdapter(task);
// ... etc
}
}
You might also be interested in reading Tomas Mikula's blog, though I don't think you can use his library directly to implement binding to a POJO.

ListView is not reflecting changes

I created a TableView a while back and registered Properties to each of the TableColumns. Editing of the internal data reflected itself back in the TableView just fine.
With a ListView, however, it is a different story. The changes are not being shown right away unless I close the frame and open it again.
My ListView consists of ActionSteps. Note that I used the Javafx beans properties.
package application.objects;
import java.time.LocalDate;
import java.util.ArrayList;
import java.util.function.IntPredicate;
import javafx.beans.property.IntegerProperty;
import javafx.beans.property.ObjectProperty;
import javafx.beans.property.SimpleIntegerProperty;
import javafx.beans.property.SimpleObjectProperty;
import javafx.beans.property.SimpleStringProperty;
import javafx.beans.property.StringProperty;
public class ActionStep {
private StringProperty actionStepID;
private ObjectProperty<LocalDate> dateSet, dateFinished;
private StringProperty stepName;
private IntegerProperty completion;
private ArrayList<StepComment> comments;
public ActionStep(String name) {
actionStepID = new SimpleStringProperty();
stepName = new SimpleStringProperty();
dateSet = new SimpleObjectProperty<LocalDate>();
dateFinished = new SimpleObjectProperty<LocalDate>();
completion = new SimpleIntegerProperty();
stepName.setValue(name);
}
public void setName(String name) {
stepName.setValue(name);
}
public String getName() {
return stepName.getValue();
}
public StringProperty stepNameProperty() {
return actionStepID;
}
public void setID(String id) {
actionStepID.setValue(id);
}
public String getID() {
return actionStepID.get();
}
public StringProperty actionStepIDProperty() {
return actionStepID;
}
public void setCompletion(int percent) {
if (percent < 0 || percent > 100)
return;
completion.set(percent);
}
public int getCompletion() {
return completion.get();
}
public IntegerProperty completionProperty() {
return completion;
}
public void setDateSet(LocalDate date) {
dateSet.set(date);
}
public LocalDate getDateSet() {
return dateSet.get();
}
public ObjectProperty<LocalDate> dateSetProperty() {
return dateSet;
}
public void setDateFinished(LocalDate date) {
dateFinished.set(date);
}
public LocalDate getDateFinished() {
return dateFinished.get();
}
public ObjectProperty<LocalDate> dateFinishedProperty() {
return dateFinished;
}
public String toString() {
return stepNameProperty().get();
}
}
My ListView uses an ObservableList as well.
#FXML
private ListView<ActionStep> actionStepsListView;
private ObservableList<ActionStep> listOfSteps;
listOfSteps = FXCollections.observableArrayList();
actionStepsListView.setItems(listOfSteps);
if (plan != null) {
ArrayList<ActionStep> arrayOfSteps = plan.getStepsArrayList();
for (int i = 0; i < arrayOfSteps.size(); i++)
listOfSteps.add(arrayOfSteps.get(i));
} else
plan = new ActionPlan();
How come changes made to the ObservableList do not reflect themselves in the ListView? I noticed that the ListView called upon every object's toString() to display their values in the ListView, rather than binding it to their Properties.
What am I doing wrong? Am I supposed to override a cell factory or something?
Note that you're trying to do something more complex with the cells in your ListView than you were with the cells in the TableView. In the TableView, the objects displayed in the cells were changing, so it was easy for the cells to observe this. In the ListView, you want the cells to notice when properties that belong to the objects displayed in the cells change; this is one further step removed, so you have to do a bit of extra coding (though not much, as you'll see).
You could create a custom cell factory to bind to the stepNameProperty(), but it's tricky (you have to make sure to unbind/remove listeners from old items in the updateItem() method).
The easier way, though, which isn't well documented is to use an ObservableList with an extractor defined.
First, fix your method names: you have some weird mismatches in the code you posted. The getX/setX/xProperty method names should all match correctly. I.e. instead of
public void setName(String name) {
stepName.setValue(name);
}
public String getName() {
return stepName.getValue();
}
public StringProperty stepNameProperty() {
return actionStepID;
}
you should have
public final void setName(String name) {
stepName.setValue(name);
}
public final String getName() {
return stepName.getValue();
}
public StringProperty nameProperty() {
return stepName;
}
and similarly for the other property accessor methods. (Obviously, the names of the fields can be anything you like, as they're private.) Making the get/set methods final is good practice.
Then, create the list with an extractor. The extractor is a function that maps each element in the list to an array of Observables which the list will observe. If those values change, it will fire list updates to the list's observers. Since your ActionStep's toString() method references the nameProperty(), I assume you want the ListView to update if the nameProperty() changes. So you want to do
listOfSteps = FXCollections.observableArrayList(
actionStep -> new Observable[] { actionStep.nameProperty() } // the "extractor"
);
actionStepsListView.setItems(listOfSteps);
Note that in earlier versions of JavaFX 2.2 the ListView did not properly observe the list for update events; this was fixed (if I remember correctly) shortly prior to the release of Java 8. (Since you tagged the question JavaFX8, I assume you're using Java 8 and so you should be fine here.)
If you are not using Java 8, you can use the following (equivalent but more verbose) code:
listOfSteps = FXCollections.observableArrayList(
new Callback<ActionStep, Observable[]>() {
#Override
public Observable[] call(ActionStep actionStep) {
return new Observable[] { actionStep.nameProperty() } ;
}
});
actionStepListView.setItems(listOfSteps);
Here is sample how make listview with custom objects:
public class JavaFX_ListView extends Application {
class MyObject {
String day;
int number;
MyObject(String d, int n) {
day = d;
number = n;
}
String getDay() {
return day;
}
int getNumber() {
return number;
}
#Override
public String toString() {
return number + " " + day;
}
}
ObservableList<MyObject> myList;
// Create dummy list of MyObject
private void prepareMyList() {
myList = FXCollections.observableArrayList();
myList.add(new MyObject("Sunday", 50));
myList.add(new MyObject("Monday", 60));
myList.add(new MyObject("Tuesday", 20));
}
#Override
public void start(Stage primaryStage) {
primaryStage.setTitle("sample");
prepareMyList();
ListView<MyObject> listView = new ListView<>();
listView.setItems(myList);
Pane root = new Pane();
root.getChildren().add(listView);
primaryStage.setScene(new Scene(root, 300, 250));
primaryStage.show();
// testing
Timer timer = new Timer();
timer.schedule(new UpdateListTask(), 1000, 1000);
}
public static void main(String[] args) {
launch(args);
}
// testing
public class UpdateListTask extends TimerTask {
#Override
public void run() {
Platform.runLater(new Runnable() {
#Override
public void run() {
myList.add(new MyObject("sample", Calendar.getInstance()
.getTime().getSeconds()));
}
});
}
}
}

javafx 2 CheckBoxTableCell not working in tableview

I need some help, even my case is simple.
But i can't understand why the check box in the tableview is not geting the value.
I have get the example from the javafx ensemble
I have a class
public class ReservationObj {
private BooleanProperty tcheck;
private StringProperty tname;
private StringProperty tstatus;
private int tser;
public ReservationObj(boolean tcheck, String lname , String lBStatus, int serialinVector) {
this.tcheck = new SimpleBooleanProperty(tcheck);
this.tname = new SimpleStringProperty(lname);
this.tstatus = new SimpleStringProperty(lBStatus);
this.tser = serialinVector;
this.tcheck.addListener(new ChangeListener<Boolean>() {
#Override
public void changed(ObservableValue<? extends Boolean> ov, Boolean t, Boolean t1) {
System.out.println("The check Box is: " + t1);
}
});
}
public BooleanProperty getTcheck() {return tcheck;}
public String getTname() {return tname.getValue() ;}
public void setTname(String tname) {this.tname.set(tname);}
public String getTstatus() {return tstatus.getValue() ;}
public void setTstatus(String tstatus) {this.tstatus.set(tstatus);}
public int getTser() {return tser;}
public void setTser(int tser) { this.tser = tser;}
}
And also i have the.
public Parent createContent() {
final ObservableList<ReservationObj> ReservationList = FXCollections.observableArrayList(
new ReservationObj(true, "aaaaaaaa", "bbbbbbbbb", 1));
TableColumn RCheckCol = new TableColumn<ReservationObj, Boolean>();
RCheckCol.setCellValueFactory(new PropertyValueFactory("tcheck"));
RCheckCol.setText("aaa");
RCheckCol.setCellFactory(new Callback<TableColumn<ReservationObj, Boolean>, TableCell<ReservationObj, Boolean>>() {
public TableCell<ReservationObj, Boolean> call(TableColumn<ReservationObj, Boolean> p) {
return new CheckBoxTableCell<ReservationObj, Boolean>();
}
});
TableColumn RNameCol = new TableColumn();
RNameCol.setCellValueFactory(new PropertyValueFactory("tname"));
RNameCol.setText("bbbb");
TableColumn RAgeCol = new TableColumn();
RAgeCol.setCellValueFactory(new PropertyValueFactory("tstatus"));
RAgeCol.setText("cccc");
TableView AAView = new TableView();
AAView.setItems(ReservationList);
AAView.setEditable(true);
AAView.getColumns().addAll(RCheckCol,RNameCol,RAgeCol);
return AAView;
}
And when simple .
#Override
public void start(Stage primaryStage) {
primaryStage.setScene(new Scene(createContent()));
primaryStage.show();
}
The columns of the tableview are getting the values except from the first “Checkbox”.
Also the listener in the checkbox is not working.
I really don’t understand what I have done wrong. Because I get the example from the ensemble.
Thanks for every idea and any solution.
Elias
Your ReservationObj class doesn’t respect JavaFX Properties convention in naming methods.
If you want to bind the RCheckCol with the tcheck BooleanProperty in :
RCheckCol.setCellValueFactory(new PropertyValueFactory("tcheck"));
You have to provide a tcheckProperty method in your model class:
public BooleanProperty tcheckProperty() {
return tcheck;
}
As an example of a valid JavaFX Bean:
public class Person {
private StringProperty name = new SimpleStringProperty("");
public Person(String name) {
this.name.setValue(name);
}
public String getName() {
return name.getValue();
}
public void setName(String name) {
this.name.setValue(name);
}
public StringProperty nameProperty() {
return name;
}
}

Categories

Resources