JavaFX: GridPane, ObservableList and ListChangeListener

My question is about the proper use of ObservableList and ListChangeListeners in JavaFX (JavaFX 8, although I think it would be similar to 2.x). Since I'm a JavaFX beginner, I ask if I understood correctly how to use them.

Suppose I have a list of user objects (let them be called Slots) that I want to display in GridPane: each slot will know where in the GridPane grid it should go (= row and column data).

There will be an initial (Array) list of slots, but since the contents of the list can be changed, I believe it makes sense to create an ObservableList and attach a ListChangeListener to it. However, I am a bit puzzled by what to do with methods change.wasUpdated(), etc.

Does the following setting make sense:

public class SlotViewController {

@FXML
private GridPane pane;

private ObservableList<Slot> slots;

@FXML
public void initialize() {
    List<Slot> originalSlots = ...

    slots = FXCollections.observableList(originalSlots);
    slots.addListener(new ListChangeListener<Slot>() {

        @Override
        public void onChanged(ListChangeListener.Change<? extends Slot> change) {
            while (change.next()) {
                if (change.wasUpdated()) {
                    for (Slot slot : change.getList()) {
                        slot.update(pane);
                    }
                } else {
                    for (Slot removedSlot : change.getRemoved()) {
                        removedSlot.removeFrom(pane);
                    }

                    for (Slot addedSlot : change.getAddedSubList()) {
                        addedSlot.addTO(pane);
                    }
                } 
            }
        }

    });
}

in which the slot will then have methods update(GridPane pane), addTO(GridPane pane)and removeFrom (GridPane panel), which will look something like this:

(I don't know what to do with update(GridPane pane).)

public class Slot {

...

    public void addTo(GridPane pane) {
        // slot contents might be e.g. a couple of String labels in a HBox.
        pane.add(this.getSlotContents(), this.getColumn(), this.getRow());
    }

    public void removeFrom(GridPane pane) {
        pane.getChildren().remove(this);
        // ?
    }

    public void update(GridPane pane) {
        // ????
    }
}

(1) In general, will this work, or am I missing something? Is this onChanged(ListChangeListener ...)supposed to be used? (2) If so, how should I handle the update method?

+4
source share
1 answer

(1) In general, will this work, or am I missing something?

Yes, it looks like it should work.

(2) If so, how should I handle the update method?

You probably don't need to.

ObservableList update events and extractors:

-, , ( Javadocs) ObservableList ( ObservableList ). , , . ObservableList - "". , , Slot a textProperty():

public class Slot {
    private final StringProperty text = new SimpleStringProperty(this, "text", "");
    public StringProperty textProperty() {
        return text ;
    }
    // ...
}

ObservableList<Slot>, , , FXCollections.observableArrayList(...) Callback

ObservableList<Slot> slots = FXCollections.observableArrayList( 
    (Slot slot) -> new Observable[] {slot.textProperty()} );

Callback ​​, Observable s: Observable , - .

GridPane Slot :

, , , Slot , , Slot. , : GridPane .

, Slot textProperty(), , getSlotContents() Label, . :

public class Slot {
    private final StringProperty text = new SimpleStringProperty(this, "text", "");
    public StringProperty textProperty() {
        return text ;
    }
    public final String getText() {
        return textProperty().get();
    }
    public final void setText(String text) {
        textProperty().set(text);
    }

    private final Label label = new Label();

    public Slot(String text) {
        label.textProperty().bind(text);
        setText(text);
    }

    public Node getSlotContents() {
        return label ;
    }
}

GridPane textProperty() Slot: Label, GridPane, .

, ListChangeListener change, wasUpdated() true; wasAdded() wasRemoved(), .

EasyBind, , Slot , :

public class Slot {
    private final IntegerProperty column = new SimpleIntegerProperty(this, "column");
    public IntegerProperty columnProperty() {
        return column ;
    }
    public final int getColumn() {
        return columnProperty().get();
    }
    public final void setColumn(int column) {
        columnProperty().set(column);
    }

    // similarly for row...

    public Slot(int column, int row) {
        column.addListener((obs, oldColumn, newColumn) -> 
            GridPane.setColumnIndex(getSlotContents(), newColumn.intValue()));
        // similarly for row
        setColumn(column);
        setRow(row);
    }

    // ...
}

ListChangeListener , GridPane getSlotContents() Slot s. ListChangeListener:

slots.addListener(new ListChangeListener<Slot>() {

    @Override
    public void onChanged(ListChangeListener.Change<? extends Slot> change) {
        while (change.next()) {
            if (change.wasAdded()) {
                for (Slot slot : change.getAddedSublist) {
                    pane.getChildren().add(slot.getSlotContents());
                }
            } else if (change.wasRemoved()) {
                for (Slot slot : change.getRemoved()) {
                    pane.getChildren().remove(slot.getSlotContents());
                }
            }
        }
    }

});

addTo removeFrom Slot.

, Bindings bindContent, ObservableList . EasyBind ObservableList, ObservableList .

,

ObservableList<Node> nodes = EasyBind.map(slots, Slot::getSlotContents);

ObservableList<Node>, getSlotContents() slots.

ListChangeListener :

Bindings.bindContent(pane.getChildren(), 
    EasyBind.map(slots, Slot::getSlotContents));
+6
source

All Articles