johnco3
johnco3

Reputation: 2642

Listening to SimpleListProperty<T> updates from a JavaFX Service

I have a JavaFX service and I want to be notified via a callback when the Service updates a custom property. Normally the way this is done is that the service has some built in properties like "state", "workDone", "value" etc. The one most similar to the one I want to emulate is the "value" named property. The "value" property is defined as follows in the service class.

    private final ObjectProperty<V> value = new SimpleObjectProperty<>(this, "value");
    @Override public final V getValue() { checkThread(); return value.get(); }
    @Override public final ReadOnlyObjectProperty<V> valueProperty() { checkThread(); return value; }


In my case I needed to listen to updates to a pair of message objects being updated by my JavaFX service thread, so I added a custom named property for each one as shown below.

    // add 'heartbeat' bound property
    private final ObjectProperty<CAServiceHeartbeatMessage> heartbeat = 
        new SimpleObjectProperty<>(this, "heartbeat", null);    
    public CAServiceHeartbeatMessage getHeartbeat() {
        return heartbeat.get();
    }
    public void setHeartbeatMessage(CAServiceHeartbeatMessage aHeartbeat) {
        heartbeat.set(aHeartbeat);
    }
    public ObjectProperty<CAServiceHeartbeatMessage> heartbeatProperty() {
        return heartbeat;
    }

    // add 'memberSystemStatus' bound property
    private final ObjectProperty<MemberSystemStatusMessage> memberSystemStatus = 
        new SimpleObjectProperty<>(this, "memberStatus", null);   
    public MemberSystemStatusMessage getMemberStatus() {
        return memberSystemStatus.get();
    }
    public void setMemberSystemMessage(MemberSystemStatusMessage aMemberSystemStatus) {
        memberSystemStatus.set(aMemberSystemStatus);
    }
    public ObjectProperty<MemberSystemStatusMessage> memberSystemStatusProperty() {
        return memberSystemStatus;
    }    

Then in the Controller class I added the following code to update the GUI based on changes to each of these properties:

// heartbeatMessageProperty listener
// Update the GUI widgets when a new CAServiceHeartbeatMessage (heartbeatMessage) is updated
mListenerService.heartbeatProperty().addListener(
    (ObservableValue<? extends CAServiceHeartbeatMessage> observer, 
    CAServiceHeartbeatMessage oldValue, CAServiceHeartbeatMessage newValue) -> {
    // this is where we need to intelligently update the appropriate 
    // heartbeat service entries
    System.out.println("updated CAServiceHeartbeatMessage");
});

// 'memberSystemMessage' bound property listener
mListenerService.memberSystemStatusProperty().addListener(
    (ObservableValue<? extends MemberSystemStatusMessage> observer, 
    MemberSystemStatusMessage oldValue, MemberSystemStatusMessage newValue) -> {
    if (newValue == null) {

. . .

My question is, how can I replace multiple SimpleObjectProperties to use a single SimpleListPropertys or an SimpleMapProperty in my JavaFX Service class. I'm not sure how, or whether I need to follow some property naming convention to expose some simple list property in my thread like I did with the 2 specific and separately named message properties. IT is probably worth noting that these messages share a common base message class - so they could be stored in a simple list type. I would also like to see an example of how I might handle the updates so that I can update the GUI when the changes occur, in particular I don'w know how to determine which of the elements in the list or map changed.

Upvotes: 0

Views: 1441

Answers (1)

jns
jns

Reputation: 6952

To listen to changes of the elements in your list, you can initalize the ObservableList with this factory method of FXCollections:

 ObservableList<Message> messages = FXCollections.observableArrayList(message -> new Observable[] { message.messageProperty() });

Creates a new empty observable list backed by an arraylist. This list reports element updates.

or:

FXCollections.observableList(backingList, message -> new Observable[] { message.messageProperty() });

Constructs an ObservableList that is backed by the specified list. Mutation operations on the ObservableList instance will be reported to observers that have registered on that instance. Note that mutation operations made directly to the underlying list are not reported to observers of any ObservableList that wraps it. This list also reports mutations of the elements in it by using extractor. Observable objects returned by extractor (applied to each list element) are listened for changes and transformed into "update" change of ListChangeListener.

javadoc

 public void changeListener() {

        ObservableList<Message> messages = FXCollections.observableArrayList(message -> new Observable[] { message.messageProperty() });

        messages.addListener((ListChangeListener<Message>) c -> {

            while (c.next()) {
                LOGGER.debug("change: {}", c);

                if (c.wasAdded()) {
                    List<? extends Message> addedSubList = c.getAddedSubList();
                }
                if (c.wasUpdated()) {
                    int from = c.getFrom();
                    int to = c.getTo();
                    List<? extends Message> subList = c.getList().subList(from, to);
                }
            }
        });

        HearBeat beat = new HearBeat("beat");

        messages.add(beat);
        messages.add(new Status("status"));

        beat.setMesssage("beat changed");
        beat.setMesssage("beat changed again");

        messages.addAll(new Status("1"), new Status("2"));
    }

    public abstract class Message {

        private StringProperty message;

        Message(String message) {
            this.message = new SimpleStringProperty(message);
        }

        public StringProperty messageProperty() {
            return message;
        }

        public String getMesssage() {
            return message.get();
        }

        public void setMesssage(String val) {
            message.set(val);
        }
    }

    public class HearBeat extends Message {

        public HearBeat(String message) {
            super(message);
        }
    }

    public class Status extends Message {

        Status(String message) {
            super(message);
        }
    }

Upvotes: 2

Related Questions