1

As the title says. Basically I have an ArrayList where DiceRoll is a class that is used successfully in a TableView to update the values as they are changed.

public class DiceRoll {
private final SimpleIntegerProperty rollNumber = new SimpleIntegerProperty();
private final SimpleIntegerProperty cubeRoll = new SimpleIntegerProperty(0);
private final SimpleIntegerProperty icosahedron = new SimpleIntegerProperty(0);
private final SimpleIntegerProperty dodecahedron = new SimpleIntegerProperty(0);

public DiceRoll(int rollNumber) {
    this.rollNumber.set(rollNumber);
}

public void incrementRoll(DiceTypes type){
    switch(type){
        case CUBE:
            this.cubeRoll.set(this.cubeRoll.getValue() + 1);
            break;
        case DODECAHEDRON:
            this.dodecahedron.set(this.dodecahedron.getValue() + 1);
            break;
        case ICOSAHEDRON:
            this.icosahedron.set(this.icosahedron.getValue() + 1);
            break;
    }
}

public int getRollNumber() {
    return rollNumber.get();
}

public SimpleIntegerProperty rollNumberProperty() {
    return rollNumber;
}

public int getCubeRoll() {
    return cubeRoll.get();
}

public SimpleIntegerProperty cubeRollProperty() {
    return cubeRoll;
}

public int getIcosahedron() {
    return icosahedron.get();
}

public SimpleIntegerProperty icosahedronProperty() {
    return icosahedron;
}

public int getDodecahedron() {
    return dodecahedron.get();
}

public SimpleIntegerProperty dodecahedronProperty() {
    return dodecahedron;
}

public void reset(){
    cubeRoll.set(0);
    icosahedron.set(0);
    dodecahedron.set(0);
}

}

Like so:

rollNumberColumn.setCellValueFactory(new PropertyValueFactory<>("rollNumber"));

This all works, although I would prefer a non-factory approach. However I can't figure out how to do the same kind of linking with a BarChart.

My BarChart has a CategoryAxis as the X axis and a NumberAxis as the Y axis. I can successfully add data with XYGraph.Series and XYGraph.Data methods but these won't update automagically. How would I do this?

edit:

    TableColumn<DiceRoll, Number> rollNumberColumn = new TableColumn<>("Roll Number");        
    rollNumberColumn.setCellValueFactory(new PropertyValueFactory<>("rollNumber"));        
    tableView.setItems(FXCollections.observableArrayList(model.getDiceRolls())); //model.getDiceRolls() returns an ArrayList<DiceRoll>
    tableView.getColumns().setAll(rollNumberColumn, cubeRollNumberColumn, dodecahedronRollNumberColumn, icosahedronRollNumberColumn);

A DiceRoll is basically initialized as a number that you can roll. I've added this in my table x20 for all 20 possible rolls and just add to it if it gets rolled. In graph form this would mean 3 graphs with on the X axis the roll number and the Y axis the amount of times that it rolled.

The factory comment is kind of irrelevant to my question but I don't really like using the factory pattern which is used here.

Edit2:

ObservableList<DiceRoll> testing = FXCollections.observableArrayList(model.getDiceRolls());
    testing.addListener((ListChangeListener)c -> System.out.println("I'm working!"));

I tried doing this as suggested by Chris but the program doesn't print anything. It could be that observableArrayList() returns a new list instead of wrapping existing references but that would mean my table wouldn't work either. What could be wrong?

Edit3:

What was wrong is that observables "change" when the value of the reference changes. And the values of an observablelist are other references which in my case get added once and get never touched again. This means that it NEVER actually changes in the lifetime of the program after startup. I figured out that I can attach listeners to the IntegerProperties in DiceRoll to do whatever the hell I want when one of them changes which is awesome and gives me much needed control.

edit4

this.model.getDiceRolls().parallelStream().forEach(diceRoll -> {
        diceRoll.cubeRollProperty().addListener((observable, oldValue, newValue) ->
                cubeSeries.getData().get(diceRoll.getRollNumber() - 1).setYValue(newValue)
        );
        diceRoll.dodecahedronRollProperty().addListener((observable, oldValue, newValue) ->
                dodecahedronSeries.getData().get(diceRoll.getRollNumber() - 1).setYValue(newValue)
        );
        diceRoll.icosahedronRollProperty().addListener((observable, oldValue, newValue) ->
                icosahedronSeries.getData().get(diceRoll.getRollNumber() - 1).setYValue(newValue)
        );
    });

This is what I used at the end to monitor changes. All it requires is that your class uses a so called *Property variable which supports listeners out of the box. The moment it changes you can run a piece of code to update your chart for example.

Tryphon
  • 161
  • 1
  • 13
  • Can you explain the relationship between your data structure and your graph a little more clearly? You presumably have an `ObservableList` as the items for the table (so you have multiple `DiceRoll` instances). So what do you want to see in the graph? What's on the category axis and what are the corresponding values for the y axis? – James_D Jun 27 '17 at 00:57
  • 1
    Also, what does "I would prefer a non-factory approach" mean? – James_D Jun 27 '17 at 00:59
  • @James_D updated with some relevant info – Tryphon Jun 27 '17 at 02:29

1 Answers1

1

If you are using an ObservableList you can add a ListChangeListener and call your chart update function when the list changes.

ObservableList<DiceRoll> myList = FxCollections.observableArrayList();
myList.addListener((ListChangeListener)(c -> { updateMyChart() }));

Then in updateMyChart() you would iterate the list:

private void updateMyChart() {

    /* Clear series */

    for(int i = 0; i < myList.size(); i++ {
        /* Create new Series */
    }

    /* Add Series */

}

And you would just simply add your DiceRoll objects to the list.

myList.add(new DiceRoll(1));

P.S - I left out the steps for creating / deleting a series as you mention you already can do that, if you need to see that too I can add it in.

Chris
  • 2,435
  • 6
  • 26
  • 49
  • Is that literally running the method on ANY change that happens to my observablelist? Is that how the TableView works under the hood? – Tryphon Jun 27 '17 at 04:12
  • I believe `TableView` works similarly. But this won't monitor changes of the objects in the list, for that see https://stackoverflow.com/questions/26838183/how-to-monitor-changes-on-objects-contained-in-an-observablelist-javafx – Chris Jun 27 '17 at 13:54
  • @Tryphon If this solved your problem make sure to select it as the solution so others can easily find it – Chris Jun 28 '17 at 22:47
  • I've added in an edit how I did it. Your solution didn't work for me unfortunately. Also wouldn't that attach a listener to the list itself so that changes to the list contents would fire the listener, and not changes in elements of the list? – Tryphon Jul 03 '17 at 10:18
  • @Tryphon correct. Maybe consider posting your edit as an answer and accepting it so it is more visible? I imagine this question will be asked again in the future – Chris Jul 03 '17 at 14:12