ListView does not reflect changes

I created TableView a back and registered Properties for each of the TableColumns. Editing internal data was reflected in TableView just fine.

With ListView, however, this is a completely different story. Changes are not displayed immediately unless I close the frame and open it again.

My ListView consists of an ActionStep. 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 also uses an ObservableList.

  @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(); 

Why are the changes made to the ObservableList not reflecting themselves in the ListView? I noticed that ListView calls every toString () object to display its values ​​in the ListView, rather than binding it to their properties.

What am I doing wrong? Should I override the factory cell or something else?

+3
java listview javafx javafx-8 observablelist
source share
2 answers

Note that you are trying to do something more complex with cells in a ListView than with cells in a TableView . In TableView objects displayed in the cells changed, so it was easy for the cameras to observe this. In ListView you want cells to notice when properties belonging to the objects displayed in the cells change; this is another step, so you need to do a little extra coding (although not as much as you will see).

You can create a custom factory cell to bind to stepNameProperty() , but it's complicated (you have to disable / remove listeners from old elements in updateItem() ).

An easier way that is poorly documented is to use an ObservableList with a specific extractor.

Correct the method names first: you have some weird inconsistencies in the code you posted. The label names getX / setX / xProperty must match correctly. That is, instead of

  public void setName ( ) {     stepName.setValue();  } public String getName() {     return stepName.getValue();  } public StringProperty stepNameProperty() {     return actionStepID;  } > 

you should have

  public final void setName ( ) {     stepName.setValue();  } public final  getName() {     return stepName.getValue();  } public StringProperty nameProperty() {     return stepName;  } > 

and similarly for other resource access methods. (Obviously, the field names can be any way you like, as they are private.) Creating get / set final methods is good practice.

Then create the list using the extractor . An extractor is a function that maps each item in a list to an Observable array that will be displayed in a list. If these values ​​change, they will update the list of updates to the list observers. Since your ActionStep toString() method refers to nameProperty() , I assume that you want the ListView update if nameProperty() changes. So you want to do

  listOfSteps = FXCollections.observableArrayList(   actionStep - > new Observable [] {actionStep.nameProperty()}// "" ); actionStepsListView.setItems(listOfSteps); > 

Note that in earlier versions of JavaFX 2.2, ListView did not honor the list of update events; this was fixed (if I remember correctly) shortly before the release of Java 8. (Since you noted the JavaFX8 question, I assume you are using Java 8, and therefore you should be fine.)

If you are not using Java 8, you can use the following (equivalent, but more verbose) code:

  listOfSteps = FXCollections.observableArrayList(    Callback < ActionStep, Observable [] >() {       @Override         Observable [] (ActionStep actionStep) {           return new Observable [] {actionStep.nameProperty()};       }   }); actionStepListView.setItems(listOfSteps); > 
+6
source share

Here is an example of how to make a 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())); } }); } } } 
+1
source share

All Articles