33 面积图
本章介绍面积图,这是另一种二轴图表类型。
与折线图类似,它将数据呈现为一系列由直线连接的点。然而,轴和线之间的区域会被填充颜色。每个数据系列都会使用不同的颜色进行填充。图33-1显示了一个具有两个数据系列的面积图。
创建面积图
要在应用程序中创建一个简单的面积图,至少需要定义两个轴,通过实例化AreaChart类创建AreaChart对象,使用XYChart.Series类创建一个或多个数据系列,并将数据分配给图表。
在实例化AreaChart类时,可以在构造函数中指定一个包含数据系列的可观察列表,或者通过在AreaChart对象上调用getData和addAll方法稍后添加系列。
示例33-1创建了一个面积图来说明温度监测数据。该示例使用了四月和五月两个时间段收集的两个数据系列。
示例33-1 创建面积图
import javafx.application.Application;
import javafx.scene.Scene;
import javafx.scene.chart.AreaChart;
import javafx.scene.chart.NumberAxis;
import javafx.scene.chart.XYChart;
import javafx.stage.Stage;
public class AreaChartSample extends Application {
@Override public void start(Stage stage) {
stage.setTitle("面积图示例");
final NumberAxis xAxis = new NumberAxis(1, 31, 1);
final NumberAxis yAxis = new NumberAxis();
final AreaChart<Number,Number> ac =
new AreaChart<>(xAxis,yAxis);
ac.setTitle("温度监测(摄氏度)");
XYChart.Series seriesApril= new XYChart.Series();
seriesApril.setName("四月");
seriesApril.getData().add(new XYChart.Data(1, 4));
seriesApril.getData().add(new XYChart.Data(3, 10));
seriesApril.getData().add(new XYChart.Data(6, 15));
seriesApril.getData().add(new XYChart.Data(9, 8));
seriesApril.getData().add(new XYChart.Data(12, 5));
seriesApril.getData().add(new XYChart.Data(15, 18));
seriesApril.getData().add(new XYChart.Data(18, 15));
seriesApril.getData().add(new XYChart.Data(21, 13));
seriesApril.getData().add(new XYChart.Data(24, 19));
seriesApril.getData().add(new XYChart.Data(27, 21));
seriesApril.getData().add(new XYChart.Data(30, 21));
XYChart.Series seriesMay = new XYChart.Series();
seriesMay.setName("五月");
seriesMay.getData().add(new XYChart.Data(1, 20));
seriesMay.getData().add(new XYChart.Data(3, 15));
seriesMay.getData().add(new XYChart.Data(6, 13));
seriesMay.getData().add(new XYChart.Data(9, 12));
seriesMay.getData().add(new XYChart.Data(12, 14));
seriesMay.getData().add(new XYChart.Data(15, 18));
seriesMay.getData().add(new XYChart.Data(18, 25));
seriesMay.getData().add(new XYChart.Data(21, 25));
seriesMay.getData().add(new XYChart.Data(24, 23));
seriesMay.getData().add(new XYChart.Data(27, 26));
seriesMay.getData().add(new XYChart.Data(31, 26));
Scene scene = new Scene(ac,800,600);
ac.getData().addAll(seriesApril, seriesMay);
stage.setScene(scene);
stage.show();
}
public static void main(String[] args) {
launch(args);
}
}
这个例子创建了两个NumberAxis对象,用于在水平和垂直轴上呈现数值数据。水平轴(X)上的值从XYChart.Data对象的第一个参数中获取,而第二个参数提供垂直轴(Y)的数据。
使用getData和addAll方法将数据系列分配给图表。因为最后添加的seriesMay数据,所以对应的绿色区域覆盖了显示四月数据的黄色区域。
编译和运行应用程序的结果如图33-2所示。
创建堆叠面积图
您可以使用StackedAreaChart类来表示面积图中的数据。该类构建的区域是堆叠的,因此每个系列相邻但不重叠。 示例33-2实现了这个任务。
示例33-2 创建堆叠面积图
import javafx.application.Application;
import javafx.scene.Scene;
import javafx.scene.chart.NumberAxis;
import javafx.scene.chart.StackedAreaChart;
import javafx.scene.chart.XYChart;
import javafx.stage.Stage;
public class StackedAreaChartSample extends Application {
final NumberAxis xAxis = new NumberAxis(1, 31, 1);
final NumberAxis yAxis = new NumberAxis();
final StackedAreaChart<Number, Number> sac =
new StackedAreaChart<>(xAxis, yAxis);
@Override
public void start(Stage stage) {
stage.setTitle("面积图示例");
sac.setTitle("温度监测(摄氏度)");
XYChart.Series<Number, Number> seriesApril =
new XYChart.Series<>();
seriesApril.setName("四月");
seriesApril.getData().add(new XYChart.Data(1, 4));
seriesApril.getData().add(new XYChart.Data(3, 10));
seriesApril.getData().add(new XYChart.Data(6, 15));
seriesApril.getData().add(new XYChart.Data(9, 8));
seriesApril.getData().add(new XYChart.Data(12, 5));
seriesApril.getData().add(new XYChart.Data(15, 18));
seriesApril.getData().add(new XYChart.Data(18, 15));
seriesApril.getData().add(new XYChart.Data(21, 13));
seriesApril.getData().add(new XYChart.Data(24, 19));
seriesApril.getData().add(new XYChart.Data(27, 21));
seriesApril.getData().add(new XYChart.Data(30, 21));
XYChart.Series<Number, Number> seriesMay =
new XYChart.Series<>();
seriesMay.setName("五月");
seriesMay.getData().add(new XYChart.Data(1, 20));
seriesMay.getData().add(new XYChart.Data(3, 15));
seriesMay.getData().add(new XYChart.Data(6, 13));
seriesMay.getData().add(new XYChart.Data(9, 12));
seriesMay.getData().add(new XYChart.Data(12, 14));
seriesMay.getData().add(new XYChart.Data(15, 18));
seriesMay.getData().add(new XYChart.Data(18, 25));
seriesMay.getData().add(new XYChart.Data(21, 25));
seriesMay.getData().add(new XYChart.Data(24, 23));
seriesMay.getData().add(new XYChart.Data(27, 26));
seriesMay.getData().add(new XYChart.Data(31, 26));
Scene scene = new Scene(sac, 800, 600);
sac.getData().addAll(seriesApril, seriesMay);
stage.setScene(scene);
stage.show();
}
public static void main(String[] args) {
launch(args);
}
}
当您编译和运行此应用程序时,它会创建如图33-3所示的图表。
将图33-3中显示的数据与图33-2中的相同数据进行比较。堆叠面积图中的区域显示了沿水平轴的任意点上垂直轴上的累积值。例如,堆叠面积图中5月15日垂直轴上显示的值为36,这不对应于当天的实际温度。该值表示4月15日和5月15日的温度的累积结果。
在开发JavaFX应用程序中的面积图时,请记住垂直轴上的数据根据面积图的类型(AreaChart或StackedAreaChart)进行解释。选择最适合应用程序任务的数据表示形式。
设置轴和刻度属性
温度监控应用程序的输出在图33-2和图33-3中以默认的双精度格式呈现轴上的数值,而不是以用户友好的方式呈现。例如,月份应该是整数,范围在1到31之间,而不是浮点数。
JavaFX SDK API提供了几种方法来调整图表轴上呈现的值的外观。 图33-4显示了图表轴的主要元素,包括刻度线和刻度标签,用于指示范围的数值。
您可以使用NumberAxis类的构造函数或相应的方法来指定数值范围中的最小值和最大值,如示例33-3所示。
示例33-3 指定水平轴的数据范围
//使用NumberAxis构造函数 final NumberAxis xAxis = new NumberAxis(1, 31, 1); //使用相应的方法 xAxis.setLowerBound(1); xAxis.setUpperBound(31); xAxis.setTickUnit(1);
当使用NumberAxis类的三个参数构造函数时,请记住第一个参数定义范围中的最小值,第二个参数是范围中的最大值,第三个参数定义刻度单位,即轴上两个刻度线之间的值。
此外,如果您想禁止在水平轴上显示次刻度线,则将minorTickCount属性设置为0,如示例33-4所示。
此属性定义每个主刻度线之间要显示的次刻度线的数量。通过将其值设置为0,您可以禁用水平轴上的次刻度线。
当您将来自示例33-3和示例33-4的代码行添加到温度监控应用程序中时,水平轴将如图33-5所示发生变化。
如果您的应用程序不需要显示刻度标签,请使用setTickLabelsVisible方法并将值设置为false。同样,如果您不希望刻度标记可见,请使用setTickMarkVisible方法并将值设置为false。
使用示例33-5中显示的代码行来调整垂直轴的值范围。
您还可以调整刻度标记,使次要刻度标记和主要刻度标记具有相等的长度。使用示例33-6中显示的tickLength和minorTickLength属性。
添加负值
由于温度监控应用程序中的垂直轴是使用NumberAxis类创建的,因此可以为面积图数据指定负值。
按照示例 33-7中所示创建另一个数据系列。
示例 33-7 添加一系列带有负值的数据
import javafx.application.Application;
import javafx.scene.Scene;
import javafx.scene.chart.AreaChart;
import javafx.scene.chart.NumberAxis;
import javafx.scene.chart.XYChart;
import javafx.stage.Stage;
public class AreaChartSample extends Application {
@Override public void start(Stage stage) {
stage.setTitle("面积图示例");
final NumberAxis xAxis = new NumberAxis(1, 31, 1);
xAxis.setMinorTickCount(0);
final NumberAxis yAxis = new NumberAxis(-5, 27, 5);
yAxis.setMinorTickLength(yAxis.getTickLength());
yAxis.setForceZeroInRange(false);
final AreaChart<Number,Number> ac =
new AreaChart<Number,Number>(xAxis,yAxis);
ac.setTitle("温度监测(摄氏度)");
XYChart.Series seriesApril= new XYChart.Series();
seriesApril.setName("四月");
seriesApril.getData().add(new XYChart.Data(0, 4));
seriesApril.getData().add(new XYChart.Data(3, 10));
seriesApril.getData().add(new XYChart.Data(6, 15));
seriesApril.getData().add(new XYChart.Data(9, 8));
seriesApril.getData().add(new XYChart.Data(12, 5));
seriesApril.getData().add(new XYChart.Data(15, 18));
seriesApril.getData().add(new XYChart.Data(18, 15));
seriesApril.getData().add(new XYChart.Data(21, 13));
seriesApril.getData().add(new XYChart.Data(24, 19));
seriesApril.getData().add(new XYChart.Data(27, 21));
seriesApril.getData().add(new XYChart.Data(30, 21));
XYChart.Series seriesMay = new XYChart.Series();
seriesMay.setName("五月");
seriesMay.getData().add(new XYChart.Data(0, 20));
seriesMay.getData().add(new XYChart.Data(3, 15));
seriesMay.getData().add(new XYChart.Data(6, 13));
seriesMay.getData().add(new XYChart.Data(9, 12));
seriesMay.getData().add(new XYChart.Data(12, 14));
seriesMay.getData().add(new XYChart.Data(15, 18));
seriesMay.getData().add(new XYChart.Data(18, 25));
seriesMay.getData().add(new XYChart.Data(21, 25));
seriesMay.getData().add(new XYChart.Data(24, 23));
seriesMay.getData().add(new XYChart.Data(27, 26));
seriesMay.getData().add(new XYChart.Data(31, 26));
XYChart.Series seriesMarch = new XYChart.Series();
seriesMarch.setName("三月");
seriesMarch.getData().add(new XYChart.Data(0, -2));
seriesMarch.getData().add(new XYChart.Data(3, -4));
seriesMarch.getData().add(new XYChart.Data(6, 0));
seriesMarch.getData().add(new XYChart.Data(9, 5));
seriesMarch.getData().add(new XYChart.Data(12, -4));
seriesMarch.getData().add(new XYChart.Data(15, 6));
seriesMarch.getData().add(new XYChart.Data(18, 8));
seriesMarch.getData().add(new XYChart.Data(21, 14));
seriesMarch.getData().add(new XYChart.Data(24, 4));
seriesMarch.getData().add(new XYChart.Data(27, 6));
seriesMarch.getData().add(new XYChart.Data(31, 6));
Scene scene = new Scene(ac,800,600);
ac.getData().addAll(seriesMarch, seriesApril, seriesMay);
stage.setScene(scene);
stage.show();
}
public static void main(String[] args) {
launch(args);
}
}
图33-7演示了修改后的温度监控应用程序,用于显示三个月的天气数据:三月、四月和五月。
样式化面积图
示例33-7中每个月的颜色是根据在addAll方法中声明的相应数据系列的顺序来定义的。这就是为什么图33-7中的三月份面积是黄色的。您可以通过CSS设置AreaChart对象的颜色。
创建Chart.css文件,并将其保存在AreaChartSample应用程序的主类所在的同一目录中。将示例33-8中显示的行添加到Chart.css文件中。
示例33-8 面积图的CSS样式
.default-color0.chart-area-symbol { -fx-background-color: #e9967a, #ffa07a; }
.default-color1.chart-area-symbol { -fx-background-color: #f0e68c, #fffacd; }
.default-color2.chart-area-symbol { -fx-background-color: #dda0dd, #d8bfd855; }
.default-color0.chart-series-area-line { -fx-stroke: #e9967a; }
.default-color1.chart-series-area-line { -fx-stroke: #f0e68c; }
.default-color2.chart-series-area-line { -fx-stroke: #dda0dd; }
.default-color0.chart-series-area-fill { -fx-fill: #ffa07a55; }
.default-color1.chart-series-area-fill { -fx-fill: #fffacd55; }
.default-color2.chart-series-area-fill { -fx-fill: #d8bfd855; }
chart-area-symbol CSS类定义了图表图例中特定数据系列的符号的参数。示例33-8设置了图表图例中圆圈的内部和外部颜色。
chart-series-area-line CSS类设置了面积图线的参数。在这个例子中,设置了线条描边的颜色。chart-series-area-fill CSS类定义了区域的颜色和不透明度级别。
通过使用Scene类的getStylesheets()方法,将这些样式应用于AreaChartSample应用程序,如示例33-9所示。
编译并运行此应用程序会产生修改后的面积图外观,如图33-8所示。
您可以从使用CSS样式来为JavaFX应用程序设置图表样式了解更多信息。
相关API文档

