1

right now I have the following:

ObservableList<Person> personsList;

and my UI for displaying the person list is tied to personsList.

Person is something like below:

class Person {
    Name name
    // other details
    List<SomeItem> list;
}

// Item is immutable, but SomeItem can mutate by setting and getting the Items
class SomeItem {
    Item item
    Item item2
}

The issue is SomeItem is mutable, so I would want any changes to SomeItem be propagated to the original personsList.

How would I achieve something like that?? Based on googling I kind of have the following modifications, but I am not sure if they work!

class Person {
    Name name
    // other details
    List<SomeItem> list;  // <-- change to ObservableListValue<SomeItem>
}

// Item is immutable, but SomeItem can mutate by setting and getting the Items
class SomeItem {
    Item item   // <-- change to ObservablePropertyBase<Item>
    Item item2  // <-- change to ObservablePropertyBase<Item>
}

From what I understand, this changes would make SomeItem report when any ObservablePropertyBase<Item> changes, and then ObservableListValue<SomeItem> would propagate this changes up, which would be caught by personsList?

Edit: Question 2: Is it possible to force refresh personsList? Lets say I make an overall update to a specific SomeItem, then I can refresh the entire personsList?

hkcode
  • 309
  • 1
  • 8
  • 3
    one problem per question, please - and provide a [mcve] of what you have tried so far (f.i. if you know the list field in Person should be observable, then the way to go is to make it of type observableList :) Let someItem expose its properties as .. well observableValues at least and configure the observableList in Person with an extractor on those observables (then the list will notify its listeners with change events of type wasUpdated whenever an observed property of someItem is changed. – kleopatra Oct 20 '21 at 12:08
  • 2
    I agree with kleopatra on this. These are decent questions and will get answered, but it best to have them as separate questions, each with a specific [mcve] (e.g. complete code which could be copy and pasted with no change to replicate just the issue). Also, can you explain "changes to SomeItem be propagated to the original personsList", what exactly does that mean (again you can clarify it an edit to the question if you wish). – jewelsea Oct 20 '21 at 12:18
  • 2
    For example, write the (failing) text case for your question (it won't have any UI) and put the code for it in the question. Then somebody could post and answer which adjusts the code to make it work as expected and the test will pass. – jewelsea Oct 20 '21 at 12:47
  • 2
    Dose [this](https://stackoverflow.com/questions/26730034/java-8-observable-list-invalidation-listener-nor-change-listener-is-called-in/26734379#26734379) answer one of your questions ? – c0der Oct 20 '21 at 16:27

1 Answers1

2

You can fire Change events by creating an ObservableList with an extractor.

Here is an example:

Name.java:

public class Name {
   
    private final String name;
   
    public Name(String name) {
        this.name = name;
    }
   
    public final String getName() {
        return name;
    }

}

Item.java:

public class Item {
    
    private final String name;
    
    public Item(String name) {
        this.name = name;
    }
   
    public final String getName() {
        return name;
    }
    
    @Override
    public String toString() {
        return name;
    }

}

SomeItem.java:

public class SomeItem {
    
    private final ObjectProperty<Item> item1 = new SimpleObjectProperty<>(this, "item1");
    private final ObjectProperty<Item> item2 = new SimpleObjectProperty<>(this, "item2");
    
    public SomeItem(Item item1, Item item2) {
        this.item1.set(item1);
        this.item2.set(item2);
    }
    
    public final ObjectProperty<Item> item1Property() {
        return item1;
    }
    
    public final Item getItem1() {
        return item1.get();
    }
    
    public final void setItem1(Item item) {
        item1.set(item);
    }
    
    public final ObjectProperty<Item> item2Property() {
        return item2;
    }
    
    public final Item getItem2() {
        return item2.get();
    }
    
    public final void setItem2(Item item) {
        item2.set(item);
    }
    
    @Override
    public String toString() {
        return "[" + item1.get() + ", " + item2.get() + "]";
    }
    
}

Person.java:

public class Person {
    
    private final Name name;
    
    private final ObservableList<SomeItem> someItems = FXCollections.observableArrayList(someItem -> 
                    new Observable[]{someItem.item1Property(), someItem.item2Property()});
    
    public Person(Name name, SomeItem... someItems) {
        this.name = name;
        this.someItems.addAll(someItems);
    }
    
    public final Name getName() {
        return name;
    }
    
    public final ObservableList<SomeItem> getSomeItems() {
        return someItems;
    }
    
    @Override
    public String toString() {
        return "[name=" + name.getName() + ", someItems=" + someItems + "]";
    }

}

App.java:

public class App extends Application {

    @Override
    public void start(Stage stage) {

        SomeItem someItem1 = new SomeItem(new Item("item1"), new Item("item2"));
        SomeItem someItem2 = new SomeItem(new Item("item3"), new Item("item4"));     
        SomeItem someItem3 = new SomeItem(new Item("item5"), new Item("item6"));
        SomeItem someItem4 = new SomeItem(new Item("item7"), new Item("item8"));
    
        Person person1 = new Person(new Name("person1"), someItem1, someItem2);
        Person person2 = new Person(new Name("person2"), someItem3, someItem4);

        ObservableList<Person> persons = FXCollections.observableArrayList(person -> 
            new Observable[]{person.someItemsProperty()});
    
        persons.addAll(person1, person2);

        persons.addListener((ListChangeListener<Person>) c -> {
            while (c.next()) {
                if (c.wasUpdated()) {
                    System.out.println("Updated persons:");
                    IntStream.range(c.getFrom(), c.getTo())
                            .mapToObj(index -> "Person at index " + index + " was updated to: " + c.getList().get(index))
                            .forEach(System.out::println);
                }
            }
        });

        // Update items to trigger change event for testing
        someItem1.setItem1(new Item("item1Updated"));
        someItem4.setItem2(new Item("item8Updated"));

    }

    public static void main(String[] args) {
        launch();
    }

}

Output:

Updated persons:
Person at index 0 was updated to: [name=person1, someItems=[[item1Updated, item2], [item3, item4]]]
Updated persons:
Person at index 1 was updated to: [name=person2, someItems=[[item5, item6], [item7, item8Updated]]]
Oboe
  • 2,643
  • 2
  • 9
  • 17