【发布时间】:2018-09-03 16:24:56
【问题描述】:
我想像 here 那样堆叠两个不同的 XYCharts。
然而,在这个例子中,坐标轴的边界是相同的,数据是静态的。
就我而言,我有要绘制的动态数据:新值会在可用时添加到数据系列中。因此,当新数据到达时,y 轴(例如)会更新。
而且,这两个数据集并不完全在同一范围内。
这是第一次尝试:
import javafx.application.Application;
import javafx.application.Platform;
import javafx.collections.FXCollections;
import javafx.scene.Scene;
import javafx.scene.chart.BarChart;
import javafx.scene.chart.CategoryAxis;
import javafx.scene.chart.LineChart;
import javafx.scene.chart.NumberAxis;
import javafx.scene.chart.XYChart;
import javafx.scene.layout.StackPane;
import javafx.stage.Stage;
/**
* Demonstrates how to draw layers of XYCharts.
* https://forums.oracle.com/forums/thread.jspa?threadID=2435995 "Using StackPane to layer more different type charts"
*/
public class LayeredXyChartsSample extends Application {
private XYChart.Series<String, Number> barSeries;
private XYChart.Series<String, Number> lineSeries;
public static void main(String[] args) { launch(args); }
@Override public void start(Stage stage) {
initSeries();
// Close the application when the window is closed
stage.setOnCloseRequest(t -> {
Platform.exit();
System.exit(0);
});
stage.setScene(
new Scene(
layerCharts(
createBarChart(),
createLineChart()
)
)
);
stage.show();
updateSeries();
}
@SuppressWarnings("unchecked")
private void initSeries() {
barSeries = new XYChart.Series(
FXCollections.observableArrayList(
new XYChart.Data("Jan", 2),
new XYChart.Data("Feb", 10),
new XYChart.Data("Mar", 8),
new XYChart.Data("Apr", 4),
new XYChart.Data("May", 7),
new XYChart.Data("Jun", 5),
new XYChart.Data("Jul", 4),
new XYChart.Data("Aug", 8),
new XYChart.Data("Sep", 16.5),
new XYChart.Data("Oct", 13.9),
new XYChart.Data("Nov", 17),
new XYChart.Data("Dec", 10)
)
);
lineSeries = new XYChart.Series(
FXCollections.observableArrayList(
new XYChart.Data("Jan", 1),
new XYChart.Data("Feb", 2),
new XYChart.Data("Mar", 1.5),
new XYChart.Data("Apr", 3),
new XYChart.Data("May", 2.5),
new XYChart.Data("Jun", 5),
new XYChart.Data("Jul", 4),
new XYChart.Data("Aug", 8),
new XYChart.Data("Sep", 6.5),
new XYChart.Data("Oct", 13),
new XYChart.Data("Nov", 10),
new XYChart.Data("Dec", 20)
)
);
}
private void updateSeries() {
new Thread(() -> {
for (int i = 0; i < 100; i++) {
try {
Thread.sleep(500);
} catch (InterruptedException e) {
e.printStackTrace();
}
final int index = i;
final double value = 20 * Math.random();
Platform.runLater(() -> {
barSeries.getData().remove(0);
barSeries.getData().add(new XYChart.Data<>(String.valueOf(index), value));
lineSeries.getData().remove(0);
lineSeries.getData().add(new XYChart.Data<>(String.valueOf(index), value * 2));
});
}
Platform.exit();
System.exit(0);
}).start();
}
private NumberAxis createYaxis() {
final NumberAxis axis = new NumberAxis();
axis.setAutoRanging(true);
axis.setPrefWidth(35);
axis.setMinorTickCount(10);
axis.setTickLabelFormatter(new NumberAxis.DefaultFormatter(axis) {
@Override public String toString(Number object) {
return String.format("%7.2f", object.floatValue());
}
});
return axis;
}
@SuppressWarnings("unchecked")
private BarChart<String, Number> createBarChart() {
final BarChart<String, Number> chart = new BarChart<>(new CategoryAxis(), createYaxis());
setDefaultChartProperties(chart);
chart.getData().addAll(barSeries);
return chart;
}
@SuppressWarnings("unchecked")
private LineChart<String, Number> createLineChart() {
final LineChart<String, Number> chart = new LineChart<>(new CategoryAxis(), createYaxis());
setDefaultChartProperties(chart);
chart.setCreateSymbols(false);
chart.getData().addAll(lineSeries);
return chart;
}
private void setDefaultChartProperties(final XYChart<String, Number> chart) {
chart.setLegendVisible(false);
chart.setAnimated(false);
}
@SafeVarargs
private final StackPane layerCharts(final XYChart<String, Number>... charts) {
for (int i = 1; i < charts.length; i++) {
configureOverlayChart(charts[i]);
}
StackPane stackpane = new StackPane();
stackpane.getChildren().addAll(charts);
return stackpane;
}
private void configureOverlayChart(final XYChart<String, Number> chart) {
chart.setAlternativeRowFillVisible(false);
chart.setAlternativeColumnFillVisible(false);
chart.setHorizontalGridLinesVisible(false);
chart.setVerticalGridLinesVisible(false);
chart.getXAxis().setVisible(false);
chart.getYAxis().setVisible(false);
chart.getStylesheets().addAll(getClass().getResource("/overlay-chart.css").toExternalForm());
}
}
结果如下:
Y 轴看起来不太好:有两个轴,因为它们不再具有相同的边界,所以它们不能正确重叠。
下一次尝试包括创建一个轴并将其分配给两个图表。一些变化:
- 创建了一个类变量:
private NumberAxis yAxis; -
createYaxis方法修改如下(为void方法,设置变量):private void createYaxis() { yAxis = new NumberAxis(); yAxis.setAutoRanging(true); yAxis.setPrefWidth(35); yAxis.setMinorTickCount(10);
yAxis.setTickLabelFormatter(new NumberAxis.DefaultFormatter(yAxis) { @Override public String toString(Number object) { return String.format("%7.2f", object.floatValue()); } });}
createYaxis方法在initSeries方法结束时被调用。-
图表是使用相同的 yAxis 创建的,例如:
BarChart chart = new BarChart(new CategoryAxis(), yAxis);
现在,yAxis 看起来不错,但是一旦折线图的新值超过轴边界,图形就不会以相同的比例显示(请注意,新的线系列值是条形系列新值的 2 倍值;图表未将其考虑在内)。
所以我的下一步是创建一个 BoundAxis 类,该类采用参考轴并在修改参考轴边界时更新它的绑定。像这样的:
public class BoundAxis<T> extends Axis<T> {
private final Axis<T> originalAxis;
public BoundAxis(Axis<T> originalAxis) {
this.originalAxis = originalAxis;
}
@Override
protected Object autoRange(double length) {
return originalAxis.autoRange(length); // Compilation error
}
@Override
protected void setRange(Object range, boolean animate) {
originalAxis.setRange(range, animate); // Compilation error
}
@Override
protected Object getRange() {
return originalAxis.getRange(); // Compilation error
}
@Override
public double getZeroPosition() {
return originalAxis.getZeroPosition();
}
@Override
public double getDisplayPosition(T value) {
return originalAxis.getDisplayPosition(value);
}
@Override
public T getValueForDisplay(double displayPosition) {
return originalAxis.getValueForDisplay(displayPosition);
}
@Override
public boolean isValueOnAxis(T value) {
return originalAxis.isValueOnAxis(value);
}
@Override
public double toNumericValue(T value) {
return originalAxis.toNumericValue(value);
}
@Override
public T toRealValue(double value) {
return originalAxis.toRealValue(value);
}
@Override
protected List<T> calculateTickValues(double length, Object range) {
return originalAxis.calculateTickValues(length, range); // Compilation error
}
@Override
protected String getTickMarkLabel(T value) {
return originalAxis.getTickMarkLabel(value); // Compilation error
}
}
但这不能编译,因为有些受保护的方法我不能调用。
最后一件事,我想要一些相当通用的东西:BoundAxis 必须是扩展 Axis,所以我不仅可以使用 NumberAxis。
编辑:这个问题与that one有关。
【问题讨论】:
-
只是为了清楚。您试图重叠两个数据没有关系的
Charts? -
确实如此。例如,您可能想要绘制布林带:tradingsim.com/wp-content/uploads/2016/01/…:它们是相关的,但范围不同。
标签: javafx