【问题标题】:Update JavaFX BarChart dynamically动态更新 JavaFX BarChart
【发布时间】:2017-06-27 00:52:00
【问题描述】:

正如标题所说。基本上我有一个 ArrayList ,其中 DiceRoll 是一个在 TableView 中成功使用的类,用于在更改值时更新值。

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);
}

}

像这样:

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

这一切都有效,尽管我更喜欢非工厂方法。但是我不知道如何使用 BarChart 进行相同类型的链接。

我的 BarChart 有一个 CategoryAxis 作为 X 轴,一个 NumberAxis 作为 Y 轴。我可以使用 XYGraph.Series 和 XYGraph.Data 方法成功添加数据,但这些方法不会自动更新。我该怎么做?

编辑:

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

DiceRoll 基本上被初始化为一个可以滚动的数字。我已经在我的表 x20 中为所有 20 个可能的滚动添加了这个,如果它滚动就添加到它。在图表形式中,这意味着 3 个图表,X 轴表示滚动次数,Y 轴表示滚动次数。

工厂评论与我的问题无关,但我不太喜欢使用这里使用的工厂模式。

编辑2:

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

我尝试按照 Chris 的建议执行此操作,但程序不打印任何内容。可能是 observableArrayList() 返回一个新列表而不是包装现有引用,但这意味着我的表也不起作用。有什么问题?

编辑3:

错误在于当引用的值发生变化时,可观察对象“变化”。并且 observablelist 的值是其他引用,在我的例子中,它们被添加一次并且再也不会被触及。这意味着它在启动后程序的生命周期中实际上永远不会改变。我发现我可以将侦听器附加到 DiceRoll 中的 IntegerProperties 以在其中一个发生变化时做任何我想做的事情,这很棒并且给了我非常需要的控制权。

编辑4

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)
        );
    });

这是我最后用来监控变化的。它所需要的只是你的类使用一个所谓的 *Property 变量,它支持开箱即用的侦听器。例如,当它发生变化时,您可以运行一段代码来更新图表。

【问题讨论】:

  • 你能更清楚地解释一下你的数据结构和你的图之间的关系吗?您大概有一个 ObservableList&lt;DiceRoll&gt; 作为表的项目(因此您有多个 DiceRoll 实例)。那么你想在图表中看到什么?类别轴上有什么,y 轴的对应值是什么?
  • 另外,“我更喜欢非工厂方法”是什么意思?
  • @James_D 更新了一些相关信息

标签: java arraylist javafx bar-chart fxml


【解决方案1】:

如果您使用ObservableList,您可以添加ListChangeListener,并在列表更改时调用您的图表更新函数。

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

然后在updateMyChart() 中,您将迭代列表:

private void updateMyChart() {

    /* Clear series */

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

    /* Add Series */

}

您只需将 DiceRoll 对象添加到列表中即可。

myList.add(new DiceRoll(1));

P.S - 我省略了创建/删除系列的步骤,正如你提到的,你已经可以做到这一点,如果你也需要看到,我可以添加它。

【讨论】:

  • 这真的是在我的 observablelist 发生的任何更改上运行该方法吗?这就是 TableView 的工作原理吗?
  • 我相信TableView 的工作原理类似。但这不会监控列表中对象的变化,请参阅stackoverflow.com/questions/26838183/…
  • @Tryphon 如果这解决了您的问题,请确保选择它作为解决方案,以便其他人可以轻松找到它
  • 我在编辑中添加了我是如何做到的。不幸的是,您的解决方案对我不起作用。也不会将侦听器附加到列表本身,以便对列表内容的更改会触发侦听器,而不是列表元素的更改?
  • @Tryphon 正确。也许考虑将您的编辑作为答案发布并接受它,以便更明显?我想这个问题将来会再次被问到
猜你喜欢
  • 1970-01-01
  • 2014-12-04
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2017-04-21
  • 1970-01-01
  • 1970-01-01
  • 2015-12-13
相关资源
最近更新 更多