我想在堆叠的条形图中可视化每月的收款与付款金额,但差异也应立即可见。
我正在使用以下示例代码:
public class Statistics extends Application {
final CategoryAxis xAxis = new CategoryAxis();
final NumberAxis yAxis = new NumberAxis();
final StackedBarChart<String, Number> sbc = new StackedBarChart<>(xAxis, yAxis);
final XYChart.Series<String, Number> incoming = new XYChart.Series<>();
final XYChart.Series<String, Number> outgoing = new XYChart.Series<>();
@Override
public void start(Stage stage) {
xAxis.setLabel("Month");
xAxis.setCategories(FXCollections.observableArrayList(
Arrays.asList("Jan", "Feb", "Mar")));
yAxis.setLabel("Value");
incoming.setName("Incoming");
incoming.getData().add(new XYChart.Data("Jan", 25601.34));
incoming.getData().add(new XYChart.Data("Feb2", 20148.82));
incoming.getData().add(new XYChart.Data("Mar2", 10000));
outgoing.setName("Outgoing");
outgoing.getData().add(new XYChart.Data("Jan", -7401.85));
outgoing.getData().add(new XYChart.Data("Feb2", -1941.19));
outgoing.getData().add(new XYChart.Data("Mar2", -5263.37));
Scene scene = new Scene(sbc, 800, 600);
sbc.getData().addAll(incoming, outgoing);
stage.setScene(scene);
stage.show();
}
}
结果是:
如您所见,负输出值显示在零以下,而不是从正输入值中减去,这使得很难看到两者之间的差异。
相反,我想要的是负值的条形图从正值的条形图的顶部开始,但是由于它们将重叠,所以还沿x轴应用了偏移量。在“Jan”系列的示例中,该外观应类似于:
我在玩
getNode().setTranslateX/Y()
,但这似乎不是一个好的解决方案,因为翻译单位不是图表中的刻度,而是其他。如何以一种优雅的方式创建一个“堆积”的条形图,如第二幅图所示?
最佳答案
您的问题真的很酷!
因此,在这里我想使用BarChart而不是StackedBarChart共享我的解决方案。它可能有点笨拙,但这是唯一为我工作的软件。
我想到的第一件事就是只是简单地取下钢筋并更改其Y坐标,但是不幸的是我们无法直接访问钢筋。因此,在研究了XYChart
的源代码之后,我发现图表的所有内容都位于Group plotContent
中,但该吸气剂是protected
。在这种情况下,要做的事情之一就是扩展类并增加方法的范围。因此(最终),代码如下:
public class Statistics extends Application {
final CategoryAxis xAxis = new CategoryAxis();
final NumberAxis yAxis = new NumberAxis();
// here I use the modified version of chart
final ModifiedBarChart<String, Number> chart = new ModifiedBarChart<>(xAxis, yAxis);
final XYChart.Series<String, Number> incoming = new XYChart.Series<>();
final XYChart.Series<String, Number> outgoing = new XYChart.Series<>();
@Override
public void start(Stage stage) {
xAxis.setLabel("Month");
xAxis.setCategories(FXCollections.observableArrayList(
Arrays.asList("Jan", "Feb", "Mar")));
yAxis.setLabel("Value");
incoming.setName("Incoming");
incoming.getData().add(new XYChart.Data("Jan", 25601.34));
incoming.getData().add(new XYChart.Data("Feb", 20148.82));
incoming.getData().add(new XYChart.Data("Mar", 10000));
outgoing.setName("Outgoing");
// To set the min value of yAxis you can either set lowerBound to 0 or don't use negative numbers here
outgoing.getData().add(new XYChart.Data("Jan", -7401.85));
outgoing.getData().add(new XYChart.Data("Feb", -1941.19));
outgoing.getData().add(new XYChart.Data("Mar", -5263.37));
chart.getData().addAll(incoming, outgoing);
Scene scene = new Scene(chart, 800, 600);
stage.setScene(scene);
stage.show();
// and here I iterate over all plotChildren elements
// note, that firstly all positiveBars come, then all negative ones
int dataSize = incoming.getData().size();
for (int i = 0; i < dataSize; i++) {
Node positiveBar = chart.getPlotChildren().get(i);
// subtract 1 to make two bars y-axis aligned
chart.getPlotChildren().get(i + dataSize).setLayoutY(positiveBar.getLayoutY() - 1);
}
}
// note, that I extend BarChart here
private static class ModifiedBarChart<X, Y> extends BarChart<X, Y> {
public ModifiedBarChart(@NamedArg("xAxis") Axis<X> xAxis, @NamedArg("yAxis") Axis<Y> yAxis) {
super(xAxis, yAxis);
}
public ModifiedBarChart(@NamedArg("xAxis") Axis<X> xAxis, @NamedArg("yAxis") Axis<Y> yAxis, @NamedArg("data") ObservableList<Series<X, Y>> data) {
super(xAxis, yAxis, data);
}
public ModifiedBarChart(@NamedArg("xAxis") Axis<X> xAxis, @NamedArg("yAxis") Axis<Y> yAxis, @NamedArg("data") ObservableList<Series<X, Y>> data, @NamedArg("categoryGap") double categoryGap) {
super(xAxis, yAxis, data, categoryGap);
}
@Override
public ObservableList<Node> getPlotChildren() {
return super.getPlotChildren();
}
}
结果: