(Scatter Chart),它是一种用一组点来表示数据的双轴图表。
每个点通过X和Y值来定义。跟其他双轴图表类似,你可以创建一组或者多组数据。图展示了一个带有三组数据的Scatter Chart。
创建散布图
要创建散布图,需要定义至少一组数据、设置横轴和纵轴、通过实例化ScatterChart类创建图表并且把数据指定给图表。例示范了怎样创建一个有两组数据的简单散布图。
/**
* @author zhaoyong
* @Date 2022/5/12
* @Description
*/
import javafx.application.Application;
import javafx.scene.Scene;
import javafx.scene.chart.NumberAxis;
import javafx.scene.chart.ScatterChart;
import javafx.scene.chart.XYChart;
import javafx.stage.Stage;
public class ScatterChartSample extends Application {
@Override public void start(Stage stage) {
stage.setTitle("散布图的实例");
final NumberAxis xAxis = new NumberAxis(0, 10, 1);//设定x轴最小值,最大值,刻度值
final NumberAxis yAxis = new NumberAxis(-100, 500, 100);//设定y轴最小值,最大值,刻度值
final ScatterChart<Number,Number> sc = new
ScatterChart<>(xAxis,yAxis);//创建一个散布图实例
xAxis.setLabel("年限");
yAxis.setLabel("收回日期");
sc.setTitle("投资概况");
XYChart.Series series1 = new XYChart.Series();//创建一个数据序列对象
series1.setName("股票");
series1.getData().add(new XYChart.Data(4.2, 193.2));//画出图的数据序列坐标(x轴值,y轴值)相当于确定一个数据点
series1.getData().add(new XYChart.Data(2.8, 33.6));
series1.getData().add(new XYChart.Data(6.2, 24.8));
series1.getData().add(new XYChart.Data(1, 14));
series1.getData().add(new XYChart.Data(1.2, 26.4));
series1.getData().add(new XYChart.Data(4.4, 114.4));
series1.getData().add(new XYChart.Data(8.5, 323));
series1.getData().add(new XYChart.Data(6.9, 289.8));
series1.getData().add(new XYChart.Data(9.9, 287.1));
series1.getData().add(new XYChart.Data(0.9, -9));
series1.getData().add(new XYChart.Data(3.2, 150.8));
series1.getData().add(new XYChart.Data(4.8, 20.8));
series1.getData().add(new XYChart.Data(7.3, -42.3));
series1.getData().add(new XYChart.Data(1.8, 81.4));
series1.getData().add(new XYChart.Data(7.3, 110.3));
series1.getData().add(new XYChart.Data(2.7, 41.2));
XYChart.Series series2 = new XYChart.Series();//创建一个数据序列对象
series2.setName("基金");
series2.getData().add(new XYChart.Data(5.2, 229.2));//画出图的数据序列坐标(x轴值,y轴值)相当于确定一个数据点
series2.getData().add(new XYChart.Data(2.4, 37.6));
series2.getData().add(new XYChart.Data(3.2, 49.8));
series2.getData().add(new XYChart.Data(1.8, 134));
series2.getData().add(new XYChart.Data(3.2, 236.2));
series2.getData().add(new XYChart.Data(7.4, 114.1));
series2.getData().add(new XYChart.Data(3.5, 323));
series2.getData().add(new XYChart.Data(9.3, 29.9));
series2.getData().add(new XYChart.Data(8.1, 287.4));
XYChart.Series series3 = new XYChart.Series();//创建一个数据序列对象
series3.setName("实业");
series3.getData().add(new XYChart.Data(5.2, 2.2));//画出图的数据序列坐标(x轴值,y轴值)相当于确定一个数据点
series3.getData().add(new XYChart.Data(2.4, 7.6));
series3.getData().add(new XYChart.Data(3.2, 49.8));
series3.getData().add(new XYChart.Data(1.8, 34));
series3.getData().add(new XYChart.Data(3.2, 26.2));
series3.getData().add(new XYChart.Data(7.4, 110.1));
series3.getData().add(new XYChart.Data(3.5, 33));
series3.getData().add(new XYChart.Data(9.3, 29.9));
series3.getData().add(new XYChart.Data(8.1, 27.4));
sc.getData().addAll(series1, series2,series3);//数据图对象上挂载前文定义的数据序列对象
Scene scene = new Scene(sc, 500, 400);//创建场景对象,上面挂载报告图对象
stage.setScene(scene);//舞台挂载场景
stage.show();//舞台展现
}
public static void main(String[] args) {
launch(args);
}
}
在这个例子中,ScatterChart对象由两个NumberAxis创建,表示数值型的年份和收益数额。数据范围和刻度单元在NumberAxis的构造方法中定义
管理图表数据
图表的数据是在程序中硬编码来指定的,不能通过用户界面来修改。可以在程序中使用UI控件来管理图表所表示的数据集,例如添加和移除一组数据。
查看代码,在该例中创建了两个按钮,分别名为“Add Series”和“Remove Series”,使用它们来控制数据集。
使用按钮来管理图表数据
直接把Scatter Chart添加到scene中,例使用VBox和HBox布局容器来排列scene中的组件。
在例中为“Add Series”按钮定义了setOnAction方法。它通过为XYChart.Series对象填充随机计算出来的值来创建一组新的数据。每一组新数据都通过add(series)方法添加到图表中
要从图表中移除数据,可以如例所示为“Remove Series”按钮定义setOnAction方法。通过随机生成的索引编号,调用Scatter Chart的remove(int)方法来移除数据。
/** * @author zhaoyong
* @Date 2022/5/12
* @Description
*/
import javafx.application.Application;
import javafx.collections.FXCollections;
import javafx.event.ActionEvent;
import javafx.geometry.Insets;
import javafx.scene.Group;
import javafx.scene.Scene;
import javafx.scene.chart.NumberAxis;
import javafx.scene.chart.ScatterChart;
import javafx.scene.chart.XYChart;
import javafx.scene.control.Button;
import javafx.scene.layout.HBox;
import javafx.scene.layout.VBox;
import javafx.stage.Stage;
public class ScatterChartSample2 extends Application {
@Override public void start(Stage stage) {
stage.setTitle("散布图实例");
final NumberAxis xAxis = new NumberAxis(0, 10, 1);//设定x轴最小值,最大值,刻度值
final NumberAxis yAxis = new NumberAxis(-100, 500, 100);//设定y轴最小值,最大值,刻度值
final ScatterChart<Number,Number> sc =
new ScatterChart<>(xAxis,yAxis);//创建一个散布图实例
xAxis.setLabel("时间周期");
yAxis.setLabel("接种率");
sc.setTitle("疫苗接种分布");
XYChart.Series series1 = new XYChart.Series();//创建一个数据序列对象
series1.setName("报名人数");
series1.getData().add(new XYChart.Data(4.2, 193.2));//画出图的数据序列坐标(x轴值,y轴值)相当于确定一个数据点
series1.getData().add(new XYChart.Data(2.8, 33.6));//画出图的数据序列坐标(x轴值,y轴值)相当于确定一个数据点
series1.getData().add(new XYChart.Data(6.2, 24.8));
series1.getData().add(new XYChart.Data(1, 14));
series1.getData().add(new XYChart.Data(1.2, 26.4));
series1.getData().add(new XYChart.Data(4.4, 114.4));
series1.getData().add(new XYChart.Data(8.5, 323));
series1.getData().add(new XYChart.Data(6.9, 289.8));
series1.getData().add(new XYChart.Data(9.9, 287.1));
series1.getData().add(new XYChart.Data(0.9, -9));
series1.getData().add(new XYChart.Data(3.2, 150.8));
series1.getData().add(new XYChart.Data(4.8, 20.8));
series1.getData().add(new XYChart.Data(7.3, -42.3));
series1.getData().add(new XYChart.Data(1.8, 81.4));
series1.getData().add(new XYChart.Data(7.3, 110.3));
series1.getData().add(new XYChart.Data(2.7, 41.2));
sc.setPrefSize(600, 800);//设置散布图的画面大小
sc.getData().addAll(series1);//默认添加一个数据序列到图上
Scene scene = new Scene(new Group());//创建一个场景并挂载一个节点组容器对象
final VBox vbox = new VBox();//创建垂直盒子布局器
final HBox hbox = new HBox();//创建水平盒子布局器
final Button add = new Button("增加接种批次");
final Button remove = new Button("删除数据");
hbox.setSpacing(10);//设置水平布局盒子中元素间隙
hbox.getChildren().addAll(add, remove);//添加增加和删除按钮
vbox.getChildren().addAll(sc, hbox);//垂直盒子上添加报告图
hbox.setPadding(new Insets(10, 10, 10, 50));//水平盒子布局器调整四个方向的内边距大小
add.setOnAction((ActionEvent e) -> {//添加数据序列按钮点击事件回调函数
if (sc.getData() == null) {//如果图中的数据显示区域为空,则创建一个数据显示区域并将数据序列装载集合挂载到数据显示区属性上
sc.setData(FXCollections.<XYChart.Series<Number,
Number>>observableArrayList());
}
ScatterChart.Series<Number, Number> series
= new ScatterChart.Series<>();//创建一个数据序列
series.setName("第 " + (sc.getData().size() + 1)+"批接种人数");//图例标题
for (int i = 0; i < 100; i++) {
series.getData().add(
new ScatterChart.Data<>(Math.random() * 100,
Math.random() * 500));//随机生成序列数据点坐标数据,并挂载到前文创建的数据序列对象上
}
remove.setOnAction((ActionEvent e2) -> {
if (!sc.getData().isEmpty())//如果数据显示区域的数据序列装载集合不为空则随机删除其中的一条数据序列
sc.getData().remove((int)(Math.random()*(sc.getData().size()-1)));
});
sc.getData().add(series);//初始化加载默认的数据序列到图上
});
((Group)scene.getRoot()).getChildren().add(vbox);//让场景的根节点节点组容器上挂载垂直盒子布局器对象
stage.setScene(scene);//场景挂载到舞台上
stage.show();//舞台显示
}
public static void main(String[] args) {
launch(args);
}
}
,程序的输出结果如图
用于展示每组数据的符号是在ScatterChart类的实现中硬编码的。展示了散布图符号之一的默认样式。
为ScatterChart符号定义样式
.default-color5.chart-symbol { /* hollow circle */
-fx-background-color: #860061, white;
-fx-background-insets: 0, 2;
-fx-background-radius: 5px;
-fx-padding: 5px;
}
你可以通过设置.default-color5.chart-symbol属性的值来改变这个符号的样式。
为图表添加效果
在javafx.scene.chart包中所有可用的图表类都是Node类的子类。因此,你可以对每一种图表应用可视化效果或者变换。查看例中的代码片段,它为Scatter Chart创建并应用了一个阴影效果。
创建并应用阴影效果
final DropShadow shadow = new DropShadow();
shadow.setOffsetX(2);
shadow.setColor(Color.GREY);
sc.setEffect(shadow);
——————————————————————————
/**
* @author zhaoyong
* @Date 2022/5/12
* @Description
*/
import javafx.application.Application;
import javafx.collections.FXCollections;
import javafx.event.ActionEvent;
import javafx.geometry.Insets;
import javafx.scene.Group;
import javafx.scene.Scene;
import javafx.scene.chart.NumberAxis;
import javafx.scene.chart.ScatterChart;
import javafx.scene.chart.XYChart;
import javafx.scene.control.Button;
import javafx.scene.effect.DropShadow;
import javafx.scene.layout.HBox;
import javafx.scene.layout.VBox;
import javafx.scene.paint.Color;
import javafx.stage.Stage;
public class ScatterChartSample2 extends Application {
@Override public void start(Stage stage) {
stage.setTitle("散布图实例");
final NumberAxis xAxis = new NumberAxis(0, 10, 1);//设定x轴最小值,最大值,刻度值
final NumberAxis yAxis = new NumberAxis(-100, 500, 100);//设定y轴最小值,最大值,刻度值
final ScatterChart<Number,Number> sc =
new ScatterChart<>(xAxis,yAxis);//创建一个散布图实例
xAxis.setLabel("时间周期");
yAxis.setLabel("接种率");
sc.setTitle("疫苗接种分布");
XYChart.Series series1 = new XYChart.Series();//创建一个数据序列对象
series1.setName("报名人数");
series1.getData().add(new XYChart.Data(4.2, 193.2));//画出图的数据序列坐标(x轴值,y轴值)相当于确定一个数据点
series1.getData().add(new XYChart.Data(2.8, 33.6));//画出图的数据序列坐标(x轴值,y轴值)相当于确定一个数据点
series1.getData().add(new XYChart.Data(6.2, 24.8));
series1.getData().add(new XYChart.Data(1, 14));
series1.getData().add(new XYChart.Data(1.2, 26.4));
series1.getData().add(new XYChart.Data(4.4, 114.4));
series1.getData().add(new XYChart.Data(8.5, 323));
series1.getData().add(new XYChart.Data(6.9, 289.8));
series1.getData().add(new XYChart.Data(9.9, 287.1));
series1.getData().add(new XYChart.Data(0.9, -9));
series1.getData().add(new XYChart.Data(3.2, 150.8));
series1.getData().add(new XYChart.Data(4.8, 20.8));
series1.getData().add(new XYChart.Data(7.3, -42.3));
series1.getData().add(new XYChart.Data(1.8, 81.4));
series1.getData().add(new XYChart.Data(7.3, 110.3));
series1.getData().add(new XYChart.Data(2.7, 41.2));
sc.setPrefSize(600, 800);//设置散布图的画面大小
sc.getData().addAll(series1);//默认添加一个数据序列到图上
Scene scene = new Scene(new Group());//创建一个场景并挂载一个节点组容器对象
final VBox vbox = new VBox();//创建垂直盒子布局器
final HBox hbox = new HBox();//创建水平盒子布局器
final Button add = new Button("增加接种批次");
final Button remove = new Button("删除数据");
hbox.setSpacing(10);//设置水平布局盒子中元素间隙
hbox.getChildren().addAll(add, remove);//添加增加和删除按钮
vbox.getChildren().addAll(sc, hbox);//垂直盒子上添加报告图
hbox.setPadding(new Insets(10, 10, 10, 50));//水平盒子布局器调整四个方向的内边距大小
add.setOnAction((ActionEvent e) -> {//添加数据序列按钮点击事件回调函数
if (sc.getData() == null) {//如果图中的数据显示区域为空,则创建一个数据显示区域并将数据序列装载集合挂载到数据显示区属性上
sc.setData(FXCollections.<XYChart.Series<Number,
Number>>observableArrayList());
}
ScatterChart.Series<Number, Number> series
= new ScatterChart.Series<>();//创建一个数据序列
series.setName("第 " + (sc.getData().size() + 1)+"批接种人数");//图例标题
for (int i = 0; i < 100; i++) {
series.getData().add(
new ScatterChart.Data<>(Math.random() * 100,
Math.random() * 500));//随机生成序列数据点坐标数据,并挂载到前文创建的数据序列对象上
}
remove.setOnAction((ActionEvent e2) -> {
if (!sc.getData().isEmpty())//如果数据显示区域的数据序列装载集合不为空则随机删除其中的一条数据序列
sc.getData().remove((int)(Math.random()*(sc.getData().size()-1)));
});
sc.getData().add(series);//初始化加载默认的数据序列到图上
});
final DropShadow shadow = new DropShadow();//创建阴影效果对象
shadow.setOffsetX(2);//设置阴影效果偏移量
shadow.setColor(Color.GREY);//设置阴影颜色
sc.setEffect(shadow);//给图添加阴影效果
((Group)scene.getRoot()).getChildren().add(vbox);//让场景的根节点节点组容器上挂载垂直盒子布局器对象
stage.setScene(scene);//场景挂载到舞台上
stage.show();//舞台显示
}
public static void main(String[] args) {
launch(args);
}
}
需要注意的是:阴影的可视化效果应用到了图表中的所有元素,包括坐标轴、刻度线和刻度值。
改变图标显示符号
Scatter Chart中的每组数据由modena.css中定义的符号表示,modena.css是JavaFX程序的默认样式表文件。然而,你可以通过实现自己的样式表来改变图表符号。
创建Chart.css文件并且保存到AreaChartSample程序主类所在的目录下,在该文件中加入例所示内容。
用CSS创建新的图表符号
.chart-symbol{-fx-stroke: #a9e200;-fx-shape: "M0,4 L2,4 L4,8 L7,0 L9,0 L4,11 Z";}
该代码片段通过定义-fx-shape参数中的SVG路径来创建符号的形状,并且设置了图表符号的笔画颜色
使用Scene类的getStylesheets()方法来将样式表应用到程序中
scene.getStylesheets().add("Chart.css");//添加样式文件
/**
* @author zhaoyong
* @Date 2022/5/12
* @Description
*/
import javafx.application.Application;
import javafx.collections.FXCollections;
import javafx.event.ActionEvent;
import javafx.geometry.Insets;
import javafx.scene.Group;
import javafx.scene.Scene;
import javafx.scene.chart.NumberAxis;
import javafx.scene.chart.ScatterChart;
import javafx.scene.chart.XYChart;
import javafx.scene.control.Button;
import javafx.scene.effect.DropShadow;
import javafx.scene.layout.HBox;
import javafx.scene.layout.VBox;
import javafx.scene.paint.Color;
import javafx.stage.Stage;
public class ScatterChartSample2 extends Application {
@Override public void start(Stage stage) {
stage.setTitle("散布图实例");
final NumberAxis xAxis = new NumberAxis(0, 10, 1);//设定x轴最小值,最大值,刻度值
final NumberAxis yAxis = new NumberAxis(-100, 500, 100);//设定y轴最小值,最大值,刻度值
final ScatterChart<Number,Number> sc =
new ScatterChart<>(xAxis,yAxis);//创建一个散布图实例
xAxis.setLabel("时间周期");
yAxis.setLabel("接种率");
sc.setTitle("疫苗接种分布");
XYChart.Series series1 = new XYChart.Series();//创建一个数据序列对象
series1.setName("报名人数");
series1.getData().add(new XYChart.Data(4.2, 193.2));//画出图的数据序列坐标(x轴值,y轴值)相当于确定一个数据点
series1.getData().add(new XYChart.Data(2.8, 33.6));//画出图的数据序列坐标(x轴值,y轴值)相当于确定一个数据点
series1.getData().add(new XYChart.Data(6.2, 24.8));
series1.getData().add(new XYChart.Data(1, 14));
series1.getData().add(new XYChart.Data(1.2, 26.4));
series1.getData().add(new XYChart.Data(4.4, 114.4));
series1.getData().add(new XYChart.Data(8.5, 323));
series1.getData().add(new XYChart.Data(6.9, 289.8));
series1.getData().add(new XYChart.Data(9.9, 287.1));
series1.getData().add(new XYChart.Data(0.9, -9));
series1.getData().add(new XYChart.Data(3.2, 150.8));
series1.getData().add(new XYChart.Data(4.8, 20.8));
series1.getData().add(new XYChart.Data(7.3, -42.3));
series1.getData().add(new XYChart.Data(1.8, 81.4));
series1.getData().add(new XYChart.Data(7.3, 110.3));
series1.getData().add(new XYChart.Data(2.7, 41.2));
sc.setPrefSize(600, 800);//设置散布图的画面大小
sc.getData().addAll(series1);//默认添加一个数据序列到图上
Scene scene = new Scene(new Group());//创建一个场景并挂载一个节点组容器对象
final VBox vbox = new VBox();//创建垂直盒子布局器
final HBox hbox = new HBox();//创建水平盒子布局器
final Button add = new Button("增加接种批次");
final Button remove = new Button("删除数据");
hbox.setSpacing(10);//设置水平布局盒子中元素间隙
hbox.getChildren().addAll(add, remove);//添加增加和删除按钮
vbox.getChildren().addAll(sc, hbox);//垂直盒子上添加报告图
hbox.setPadding(new Insets(10, 10, 10, 50));//水平盒子布局器调整四个方向的内边距大小
add.setOnAction((ActionEvent e) -> {//添加数据序列按钮点击事件回调函数
if (sc.getData() == null) {//如果图中的数据显示区域为空,则创建一个数据显示区域并将数据序列装载集合挂载到数据显示区属性上
sc.setData(FXCollections.<XYChart.Series<Number,
Number>>observableArrayList());
}
ScatterChart.Series<Number, Number> series
= new ScatterChart.Series<>();//创建一个数据序列
series.setName("第 " + (sc.getData().size() + 1)+"批接种人数");//图例标题
for (int i = 0; i < 100; i++) {
series.getData().add(
new ScatterChart.Data<>(Math.random() * 100,
Math.random() * 500));//随机生成序列数据点坐标数据,并挂载到前文创建的数据序列对象上
}
remove.setOnAction((ActionEvent e2) -> {
if (!sc.getData().isEmpty())//如果数据显示区域的数据序列装载集合不为空则随机删除其中的一条数据序列
sc.getData().remove((int)(Math.random()*(sc.getData().size()-1)));
});
sc.getData().add(series);//初始化加载默认的数据序列到图上
});
final DropShadow shadow = new DropShadow();//创建阴影效果对象
shadow.setOffsetX(2);//设置阴影效果偏移量
shadow.setColor(Color.GREY);//设置阴影颜色
sc.setEffect(shadow);//给图添加阴影效果
((Group)scene.getRoot()).getChildren().add(vbox);//让场景的根节点节点组容器上挂载垂直盒子布局器对象
scene.getStylesheets().add("Chart.css");//添加样式文件
stage.setScene(scene);//场景挂载到舞台上
stage.show();//舞台显示
}
public static void main(String[] args) {
launch(args);
}
}
编译并运行此程序将使散步图的外观改变,如图所示。
图 修改symbol之后的Scatter Chart