使用 JavaFX 的观察者模式和条形图问题
Problem with Observer Pattern and bar chart using JavaFX
我遇到了问题,我想要一个带有系列的条形图,使用观察者模式自动更新。问题是:当调用更新函数时,图表上没有任何附加内容。这是我使用的代码:
第一个 class 是为我提供数据的 class。
import java.util.Observable;
public class ChartModel extends Observable {
private String[] dataName;
private static double[] data;
public ChartModel(){
dataName=new String[4];
data=new double[4];
}
public String[] getDataName(){
return dataName;
}
public double[] getData(){
return data;
}
public void setChartData(String[] d, double[] dd){
for(int i=0; i<dataName.length; i++){
dataName[i]=d[i];
data[i]=dd[i];
}
setChanged();
notifyObservers();
}
public void updateCharData(String dataName, double newData){
int i;
for(i=0; i<dataName.length() && !this.dataName[i].equals(dataName); i++);
if(i<dataName.length())
data[i]=newData;
setChanged();
notifyObservers();
}
}
这是我的图表class:
import javafx.application.Application;
import javafx.scene.Scene;
import javafx.scene.chart.BarChart;
import javafx.scene.chart.CategoryAxis;
import javafx.scene.chart.NumberAxis;
import javafx.scene.chart.XYChart;
import javafx.stage.Stage;
import java.util.Observable;
import java.util.Observer;
public class MyBarChart extends Application implements Observer {
private String[] dataName;
private double[] data;
BarChart<String, Number> bc;
public void start(Stage stage) {
stage.setTitle("Graphique");
final CategoryAxis xAxis = new CategoryAxis();
final NumberAxis yAxis = new NumberAxis();
bc=new BarChart<String, Number>(xAxis,yAxis);
bc.setTitle("Etudiants");
xAxis.setLabel("Sections");
//xAxis.setTickLabelRotation(90);
yAxis.setLabel("Nombre d'élèves");
Scene scene = new Scene(bc,800,600);
stage.setScene(scene);
stage.show();
}
@Override
public void update(Observable o, Object arg) {
if(o instanceof ChartModel){
ChartModel cm=(ChartModel) o;
dataName=cm.getDataName();
data=cm.getData();
for(int i=0; i<dataName.length; i++){
System.out.println(dataName[i]+ " " +data[i]);
}
bc.getData().clear();
for(int i=0; i<dataName.length; i++){
XYChart.Series series1 = new XYChart.Series();
series1.setName(dataName[i]);
series1.getData().add(new XYChart.Data(data[i], dataName[i]));
bc.getData().addAll(series1);
}
}
}
public static void main(String[] args){
String[] dataName=new String[]{"Informatique","Infirmier","Kine", "Compta"};
double[] data=new double[]{10,20,30,40};
ChartModel cm=new ChartModel();
launch(args);
cm.setChartData(dataName, data);
MyBarChart bc=new MyBarChart();
cm.addObserver(bc);
//cm.updateCharData("Informatique", 50);
/* try{
Thread.sleep(5000);
}
catch (InterruptedException e){//trt
}
cm.updateCharData("Informatique", 10);
try{ Thread.sleep(5000); }
catch (InterruptedException e){//trt
}*/
}
}
你知道我能做些什么来解决这个问题吗?
非常感谢。
JavaFX 应用程序的(简化)生命周期如下:
Application.launch
被调用,这将启动工具包等。
- 已创建
Application
class 实例。
- 重复事件handling/rendering/layout等
- 当 JavaFX 确定它应该关闭时,它会进行一些清理并
Application.launch
returns。
为此main方法中的代码
launch(args);
GUI关闭后执行。
此外,由于 Application.launch
创建了它自己的应用程序实例 class,因此不会对 GUI 进行任何更新。 (您正在添加一个不同的实例作为观察者)。
另请注意,只要 JavaFX 应用程序线程上的长 运行 操作为 运行,您的应用程序就会冻结。出于这个原因,您需要确保对模型的更新是在单独的线程上完成的。这还要求您确保对 GUI 的更新是在 JavaFX 应用程序线程上完成的(出于性能原因 JavaFX 假设对 GUI 的访问仅从该线程完成。)。
你的 ChartModel
class 的某些部分看起来很奇怪:
data
是 static
但 dataName
不是
- 您似乎是在重新发明轮子:您可以简单地使用一个
LinkedHashMap<String, Double>
而不是使用 2 个数组来存储数据
- 对数据的访问不同步导致潜在的并发问题
Observable
在 Java 9 中被弃用
- 在
updateCharData
循环条件和if
条件 中使用字符串参数的长度
以下代码演示了从 start
方法开始更新并从单独的线程执行更新。它还将数据存储到 LinkedHashMap
的方式更改为避免实现类似地图的功能。
public class MyBarChart extends Application implements Observer {
BarChart<String, Number> bc;
public void start(Stage stage) {
stage.setTitle("Graphique");
final CategoryAxis xAxis = new CategoryAxis();
final NumberAxis yAxis = new NumberAxis();
bc = new BarChart<String, Number>(xAxis, yAxis);
bc.setTitle("Etudiants");
xAxis.setLabel("Sections");
//xAxis.setTickLabelRotation(90);
yAxis.setLabel("Nombre d'élèves");
Scene scene = new Scene(bc, 800, 600);
stage.setScene(scene);
stage.show();
initModel();
}
private void initModel() {
String[] dataName = new String[]{"Informatique", "Infirmier", "Kine", "Compta"};
double[] data = new double[]{10, 20, 30, 40};
ChartModel cm = new ChartModel();
cm.setChartData(dataName, data);
cm.addObserver(this);
new Thread(() -> {
cm.updateCharData("Informatique", 50);
try {
Thread.sleep(5000);
} catch (InterruptedException e) {//trt
}
cm.updateCharData("Informatique", 10);
try {
Thread.sleep(5000);
} catch (InterruptedException e) {//trt
}
}).start();
}
@Override
public void update(Observable o, Object arg) {
if (o instanceof ChartModel) {
ChartModel cm = (ChartModel) o;
Map<String, Double> data = cm.getData();
// make sure to read the data from the thread that does the updates
// or make sure the data is synchronized
final XYChart.Series<String, Number>[] series = new XYChart.Series[data.size()];
int index = 0;
for (Map.Entry<String, Double> entry : cm.getData().entrySet()) {
XYChart.Series<String, Number> series1 = new XYChart.Series<>();
series1.setName(entry.getKey());
series1.getData().add(new XYChart.Data<>(entry.getKey(), entry.getValue()));
series[index] = series1;
index++;
}
// updates to the gui on the javafx application thread
Platform.runLater(() -> bc.getData().setAll(series));
}
}
public static void main(String[] args) {
launch(args);
}
}
public class ChartModel extends Observable {
private final Map<String, Double> data = new LinkedHashMap<>();
public Map<String, Double> getData() {
return data;
}
public void setChartData(String[] d, double[] dd) {
if (d.length != dd.length) {
throw new IllegalArgumentException();
}
for (int i = 0; i < d.length; i++) {
data.put(d[i], dd[i]);
}
setChanged();
notifyObservers();
}
public void updateCharData(String dataName, double newData) {
data.put(dataName, newData);
setChanged();
notifyObservers();
}
}
我遇到了问题,我想要一个带有系列的条形图,使用观察者模式自动更新。问题是:当调用更新函数时,图表上没有任何附加内容。这是我使用的代码: 第一个 class 是为我提供数据的 class。
import java.util.Observable;
public class ChartModel extends Observable {
private String[] dataName;
private static double[] data;
public ChartModel(){
dataName=new String[4];
data=new double[4];
}
public String[] getDataName(){
return dataName;
}
public double[] getData(){
return data;
}
public void setChartData(String[] d, double[] dd){
for(int i=0; i<dataName.length; i++){
dataName[i]=d[i];
data[i]=dd[i];
}
setChanged();
notifyObservers();
}
public void updateCharData(String dataName, double newData){
int i;
for(i=0; i<dataName.length() && !this.dataName[i].equals(dataName); i++);
if(i<dataName.length())
data[i]=newData;
setChanged();
notifyObservers();
}
}
这是我的图表class:
import javafx.application.Application;
import javafx.scene.Scene;
import javafx.scene.chart.BarChart;
import javafx.scene.chart.CategoryAxis;
import javafx.scene.chart.NumberAxis;
import javafx.scene.chart.XYChart;
import javafx.stage.Stage;
import java.util.Observable;
import java.util.Observer;
public class MyBarChart extends Application implements Observer {
private String[] dataName;
private double[] data;
BarChart<String, Number> bc;
public void start(Stage stage) {
stage.setTitle("Graphique");
final CategoryAxis xAxis = new CategoryAxis();
final NumberAxis yAxis = new NumberAxis();
bc=new BarChart<String, Number>(xAxis,yAxis);
bc.setTitle("Etudiants");
xAxis.setLabel("Sections");
//xAxis.setTickLabelRotation(90);
yAxis.setLabel("Nombre d'élèves");
Scene scene = new Scene(bc,800,600);
stage.setScene(scene);
stage.show();
}
@Override
public void update(Observable o, Object arg) {
if(o instanceof ChartModel){
ChartModel cm=(ChartModel) o;
dataName=cm.getDataName();
data=cm.getData();
for(int i=0; i<dataName.length; i++){
System.out.println(dataName[i]+ " " +data[i]);
}
bc.getData().clear();
for(int i=0; i<dataName.length; i++){
XYChart.Series series1 = new XYChart.Series();
series1.setName(dataName[i]);
series1.getData().add(new XYChart.Data(data[i], dataName[i]));
bc.getData().addAll(series1);
}
}
}
public static void main(String[] args){
String[] dataName=new String[]{"Informatique","Infirmier","Kine", "Compta"};
double[] data=new double[]{10,20,30,40};
ChartModel cm=new ChartModel();
launch(args);
cm.setChartData(dataName, data);
MyBarChart bc=new MyBarChart();
cm.addObserver(bc);
//cm.updateCharData("Informatique", 50);
/* try{
Thread.sleep(5000);
}
catch (InterruptedException e){//trt
}
cm.updateCharData("Informatique", 10);
try{ Thread.sleep(5000); }
catch (InterruptedException e){//trt
}*/
}
}
你知道我能做些什么来解决这个问题吗?
非常感谢。
JavaFX 应用程序的(简化)生命周期如下:
Application.launch
被调用,这将启动工具包等。- 已创建
Application
class 实例。 - 重复事件handling/rendering/layout等
- 已创建
- 当 JavaFX 确定它应该关闭时,它会进行一些清理并
Application.launch
returns。
为此main方法中的代码
launch(args);
GUI关闭后执行。
此外,由于 Application.launch
创建了它自己的应用程序实例 class,因此不会对 GUI 进行任何更新。 (您正在添加一个不同的实例作为观察者)。
另请注意,只要 JavaFX 应用程序线程上的长 运行 操作为 运行,您的应用程序就会冻结。出于这个原因,您需要确保对模型的更新是在单独的线程上完成的。这还要求您确保对 GUI 的更新是在 JavaFX 应用程序线程上完成的(出于性能原因 JavaFX 假设对 GUI 的访问仅从该线程完成。)。
你的 ChartModel
class 的某些部分看起来很奇怪:
data
是static
但dataName
不是- 您似乎是在重新发明轮子:您可以简单地使用一个
LinkedHashMap<String, Double>
而不是使用 2 个数组来存储数据 - 对数据的访问不同步导致潜在的并发问题
Observable
在 Java 9 中被弃用
- 在
updateCharData
循环条件和if
条件 中使用字符串参数的长度
以下代码演示了从 start
方法开始更新并从单独的线程执行更新。它还将数据存储到 LinkedHashMap
的方式更改为避免实现类似地图的功能。
public class MyBarChart extends Application implements Observer {
BarChart<String, Number> bc;
public void start(Stage stage) {
stage.setTitle("Graphique");
final CategoryAxis xAxis = new CategoryAxis();
final NumberAxis yAxis = new NumberAxis();
bc = new BarChart<String, Number>(xAxis, yAxis);
bc.setTitle("Etudiants");
xAxis.setLabel("Sections");
//xAxis.setTickLabelRotation(90);
yAxis.setLabel("Nombre d'élèves");
Scene scene = new Scene(bc, 800, 600);
stage.setScene(scene);
stage.show();
initModel();
}
private void initModel() {
String[] dataName = new String[]{"Informatique", "Infirmier", "Kine", "Compta"};
double[] data = new double[]{10, 20, 30, 40};
ChartModel cm = new ChartModel();
cm.setChartData(dataName, data);
cm.addObserver(this);
new Thread(() -> {
cm.updateCharData("Informatique", 50);
try {
Thread.sleep(5000);
} catch (InterruptedException e) {//trt
}
cm.updateCharData("Informatique", 10);
try {
Thread.sleep(5000);
} catch (InterruptedException e) {//trt
}
}).start();
}
@Override
public void update(Observable o, Object arg) {
if (o instanceof ChartModel) {
ChartModel cm = (ChartModel) o;
Map<String, Double> data = cm.getData();
// make sure to read the data from the thread that does the updates
// or make sure the data is synchronized
final XYChart.Series<String, Number>[] series = new XYChart.Series[data.size()];
int index = 0;
for (Map.Entry<String, Double> entry : cm.getData().entrySet()) {
XYChart.Series<String, Number> series1 = new XYChart.Series<>();
series1.setName(entry.getKey());
series1.getData().add(new XYChart.Data<>(entry.getKey(), entry.getValue()));
series[index] = series1;
index++;
}
// updates to the gui on the javafx application thread
Platform.runLater(() -> bc.getData().setAll(series));
}
}
public static void main(String[] args) {
launch(args);
}
}
public class ChartModel extends Observable {
private final Map<String, Double> data = new LinkedHashMap<>();
public Map<String, Double> getData() {
return data;
}
public void setChartData(String[] d, double[] dd) {
if (d.length != dd.length) {
throw new IllegalArgumentException();
}
for (int i = 0; i < d.length; i++) {
data.put(d[i], dd[i]);
}
setChanged();
notifyObservers();
}
public void updateCharData(String dataName, double newData) {
data.put(dataName, newData);
setChanged();
notifyObservers();
}
}