如何解决使用 JavaFX 在 Scene Builder 中绘制图表的问题
How to solve problem with drawing Chart in Scene Builder with JavaFX
我的程序有问题。
我想在提供数据(方程的系数)后绘制图表。
我尝试更改我的导入(它对一些变量有帮助)
我将 java.awt* 更改为 javafx.scene ...(很少导入)。
FXML 文件:
<?xml version="1.0" encoding="UTF-8"?>
<?import javafx.scene.chart.CategoryAxis?>
<?import javafx.scene.chart.LineChart?>
<?import javafx.scene.chart.NumberAxis?>
<?import javafx.scene.control.Button?>
<?import javafx.scene.control.Label?>
<?import javafx.scene.control.TextField?>
<?import javafx.scene.layout.AnchorPane?>
<?import javafx.scene.text.Font?>
<AnchorPane maxHeight="-Infinity" maxWidth="-Infinity" minHeight="-Infinity" minWidth="-Infinity" prefHeight="700.0" prefWidth="800.0" xmlns="http://javafx.com/javafx/8.0.172-ea" xmlns:fx="http://javafx.com/fxml/1" fx:controller="grahps.Controller">
<children>
<TextField fx:id="factorA" layoutX="24.0" layoutY="598.0" prefHeight="33.0" prefWidth="106.0" text="a=" AnchorPane.bottomAnchor="75.0" AnchorPane.leftAnchor="24.0" AnchorPane.rightAnchor="670.0" />
<TextField fx:id="factorB" layoutX="24.0" layoutY="630.0" prefHeight="33.0" prefWidth="106.0" text="b=" AnchorPane.bottomAnchor="37.0" AnchorPane.leftAnchor="24.0" AnchorPane.rightAnchor="670.0" />
<TextField fx:id="factorC" layoutX="24.0" layoutY="674.0" prefHeight="33.0" prefWidth="106.0" text="c=" AnchorPane.bottomAnchor="1.0" AnchorPane.leftAnchor="24.0" AnchorPane.rightAnchor="670.0" />
<TextField layoutX="158.0" layoutY="592.0" prefHeight="47.0" prefWidth="120.0" text="xMin=" AnchorPane.bottomAnchor="61.0" AnchorPane.leftAnchor="158.0" AnchorPane.rightAnchor="522.0" fx:id="xMin" />
<TextField layoutX="158.0" layoutY="650.0" prefHeight="47.0" prefWidth="120.0" text="xMax=" AnchorPane.bottomAnchor="3.0" AnchorPane.leftAnchor="158.0" AnchorPane.rightAnchor="522.0" fx:id="xMax" />
<Label fx:id="label" layoutX="468.0" layoutY="629.0" prefHeight="61.0" prefWidth="276.0" text="f(x)=" AnchorPane.bottomAnchor="30.0" AnchorPane.leftAnchor="468.0" AnchorPane.rightAnchor="56.0">
<font>
<Font size="18.0" />
</font>
</Label>
<LineChart fx:id="drawChart" prefHeight="598.0" prefWidth="800.0" title="Chart">
<xAxis>
<CategoryAxis side="BOTTOM" />
</xAxis>
<yAxis>
<NumberAxis side="LEFT" />
</yAxis>
</LineChart>
<Button fx:id="button" layoutX="317.0" layoutY="612.0" mnemonicParsing="false" prefHeight="61.0" prefWidth="98.0" text="Rysuj wykres" />
</children>
</AnchorPane>
带有折线图(fx:id="drawChart")的部分生成的通信:“未解决fx:id参考
我的主要class:
package grahps;
import javafx.application.Application;
import javafx.fxml.FXMLLoader;
import javafx.scene.Parent;
import javafx.scene.Scene;
import javafx.stage.Stage;
public class Main extends Application {
private Controller controller;//
@Override
public void start(Stage stage) throws Exception {
stage.show();
System.out.println(getClass().getResource("/fxml/sample.fxml").getPath());
FXMLLoader loader = new FXMLLoader(getClass().getResource("/fxml/sample.fxml"));
loader.setController(controller);
Parent root = FXMLLoader.load(getClass().getResource("/fxml/sample.fxml"));
Scene scene = new Scene(root, 800, 800);
controller.drawChart(stage);
stage.setScene(scene);
}
public static void main(String[] args) {
launch(args);
}
}
最后是我的控制器:
package grahps;
import javafx.fxml.FXML;
import javafx.scene.Scene;
import javafx.stage.Stage;
import javafx.scene.control.TextField;
import javafx.scene.control.Label;
import javafx.scene.control.Button;
import javafx.scene.chart.NumberAxis;
import javafx.scene.chart.XYChart;
public class Controller {
@FXML
TextField factorA;
@FXML
TextField factorB;
@FXML
TextField factorC;
@FXML
TextField xMin;
@FXML
TextField xMax;
@FXML
Label label;
@FXML
Button button;
@FXML
XYChart.Series<Number, Number> chart;
//Parser Text Field -> double
double xMax1 = Double.parseDouble(xMax.getText());
double xMin1 = Double.parseDouble(xMin.getText());
double a = Double.parseDouble(factorA.getText());
double b = Double.parseDouble(factorB.getText());
double c = Double.parseDouble(factorC.getText());
@FXML
public void drawChart(Stage stage) {
XYChart.Series<Number, Number> series = chart;
series.setName("Chart");
final NumberAxis xAxis = new NumberAxis(xMin1, xMax1, 1);
final NumberAxis yAxis = new NumberAxis();
yAxis.setTickUnit(1);
xAxis.setLabel("X Label");
yAxis.setLabel("Y Label");
final javafx.scene.chart.LineChart<Number, Number> lineChart = new javafx.scene.chart.LineChart<Number, Number>(xAxis, yAxis);
double y;
String pattern;
if (a == 0 && c == 0) {
pattern = "f(x)=" + factorB;
label.setText(pattern);
} else if (c == 0) {
pattern = "f(x)=" + factorA + "x+" + factorB;
label.setText(pattern);
for (double i = xMin1; i <= xMax1; i++) {
y = a * i + b;
series.getData().add(new XYChart.Data(i, y));
}
} else {
pattern = "f(x)=" + factorC + "x^2" + factorA + "x+" + factorB;
label.setText(pattern);
for (double i = xMin1; i < xMax1; i++) {
y = a * i * i + b * i + c;
series.getData().add(new XYChart.Data(i, y));
}
}
lineChart.getData().add(series);
Scene scene = new Scene(lineChart, 800, 800);
stage.setScene(scene);
stage.setResizable(false);
stage.show();
}
}
请注意一件事:这是我的第一个 JavaFX 项目。
简单的我想填入方程的系数,点击按钮后生成方程模式+画图表。
非常感谢您的帮助。
当我删除上述方法和编译代码的@FXML 注释时,出现了这些错误:
Exception in Application start method
java.lang.reflect.InvocationTargetException
at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
at sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:62)
at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43)
at java.lang.reflect.Method.invoke(Method.java:498)
at com.sun.javafx.application.LauncherImpl.launchApplicationWithArgs(LauncherImpl.java:389)
at com.sun.javafx.application.LauncherImpl.launchApplication(LauncherImpl.java:328)
at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
at sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:62)
at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43)
at java.lang.reflect.Method.invoke(Method.java:498)
at sun.launcher.LauncherHelper$FXHelper.main(LauncherHelper.java:767)
Caused by: java.lang.RuntimeException: Exception in Application start method
at com.sun.javafx.application.LauncherImpl.launchApplication1(LauncherImpl.java:917)
at com.sun.javafx.application.LauncherImpl.lambda$launchApplication4(LauncherImpl.java:182)
at java.lang.Thread.run(Thread.java:748)
Caused by: javafx.fxml.LoadException:
/C:/Users/Damian/IdeaProjects/Graphs/target/classes/fxml/sample.fxml:14
at javafx.fxml.FXMLLoader.constructLoadException(FXMLLoader.java:2601)
at javafx.fxml.FXMLLoader.loadImpl(FXMLLoader.java:2579)
at javafx.fxml.FXMLLoader.loadImpl(FXMLLoader.java:2441)
at javafx.fxml.FXMLLoader.loadImpl(FXMLLoader.java:3214)
at javafx.fxml.FXMLLoader.loadImpl(FXMLLoader.java:3175)
at javafx.fxml.FXMLLoader.loadImpl(FXMLLoader.java:3148)
at javafx.fxml.FXMLLoader.loadImpl(FXMLLoader.java:3124)
at javafx.fxml.FXMLLoader.loadImpl(FXMLLoader.java:3104)
at javafx.fxml.FXMLLoader.load(FXMLLoader.java:3097)
at grahps.Main.start(Main.java:22)
at com.sun.javafx.application.LauncherImpl.lambda$launchApplication11(LauncherImpl.java:863)
at com.sun.javafx.application.PlatformImpl.lambda$runAndWait4(PlatformImpl.java:326)
at com.sun.javafx.application.PlatformImpl.lambda$null2(PlatformImpl.java:295)
at java.security.AccessController.doPrivileged(Native Method)
at com.sun.javafx.application.PlatformImpl.lambda$runLater3(PlatformImpl.java:294)
at com.sun.glass.ui.InvokeLaterDispatcher$Future.run(InvokeLaterDispatcher.java:95)
at com.sun.glass.ui.win.WinApplication._runLoop(Native Method)
at com.sun.glass.ui.win.WinApplication.lambda$null7(WinApplication.java:177)
... 1 more
Caused by: java.lang.NullPointerException
at grahps.Controller.<init>(Controller.java:31)
at sun.reflect.NativeConstructorAccessorImpl.newInstance0(Native Method)
at sun.reflect.NativeConstructorAccessorImpl.newInstance(NativeConstructorAccessorImpl.java:62)
at sun.reflect.DelegatingConstructorAccessorImpl.newInstance(DelegatingConstructorAccessorImpl.java:45)
at java.lang.reflect.Constructor.newInstance(Constructor.java:423)
at java.lang.Class.newInstance(Class.java:442)
at sun.reflect.misc.ReflectUtil.newInstance(ReflectUtil.java:51)
at javafx.fxml.FXMLLoader$ValueElement.processAttribute(FXMLLoader.java:927)
at javafx.fxml.FXMLLoader$InstanceDeclarationElement.processAttribute(FXMLLoader.java:971)
at javafx.fxml.FXMLLoader$Element.processStartElement(FXMLLoader.java:220)
at javafx.fxml.FXMLLoader$ValueElement.processStartElement(FXMLLoader.java:744)
at javafx.fxml.FXMLLoader.processStartElement(FXMLLoader.java:2707)
at javafx.fxml.FXMLLoader.loadImpl(FXMLLoader.java:2527)
... 17 more
Exception running application grahps.Main
Process finished with exit code 1
另外,我将 onAction="drawChart" 更改为 onAction="chart",现在我可以看到突出显示的通信:"cannot set javafx.scene.control.LineChart to chart"
代码中有几个错误,例如:
控制器中的//Parser Text Field -> double
块执行得太早=> NullPointerException
.
在 start
方法中调用 controller.drawChart(stage)
时 controller
等于 null
=> NullPointerException
.
控制器中的drawChart
方法中series
等于null
,因为chart
等于null
=>NullPointerException
.
控制器中缺少对图表的引用。这一点已经在评论中提到了。
CategoryAxis
作为x轴的类型,虽然x数据是数值。
在修复这些错误之前,应该改进架构(这将自动修复一些错误):
Controller
-class:由于每次单击按钮都必须初始化图表,然后更新图表,因此控制器中的以下更改将很有用:
实现一个initialize
方法,可以在其中进行必要的初始化。
实现一个 updateChart
方法,该方法在单击按钮时调用并更新图表。
定义对折线图的引用。
定义对两个轴的引用。
因此,Controller
-class 看起来如下:
public class Controller {
@FXML
TextField factorA;
@FXML
TextField factorB;
@FXML
TextField factorC;
@FXML
TextField xMin;
@FXML
TextField xMax;
@FXML
Label label;
@FXML
Button button;
@FXML
LineChart<Number, Number> chart;
@FXML
NumberAxis xAxis;
@FXML
NumberAxis yAxis;
@FXML
public void updateChart() {/*ToDo*/}
public void initialize(){/*ToDo*/}
}
Main
-class:在start
方法中只需要加载FXML:
@Override
public void start(Stage stage) throws Exception {
Parent root = FXMLLoader.load(getClass().getResource("/fxml/sample.fxml"));
Scene scene = new Scene(root, 800, 800);
stage.setScene(scene);
stage.show();
}
FXML:应进行以下更改:
- 将折线图的ID改为
fx:id="chart"
- 将折线图的x轴类型改为
NumberAxis
- 将
onAction="#updateChart"
添加到按钮。这会在单击按钮时调用 updateChart
方法。
- 为两个轴定义一个 ID(
fx:id="xAxis"
和 fx:id="yAxis"
)。
- 去除所有文本字段的所有非数字字符初始化(例如
text="a="
),否则会出现解析问题(使用标签或水印更有意义,例如promptText="a"
) .
然后,FXML 变为:
<AnchorPane maxHeight="-Infinity" maxWidth="-Infinity" minHeight="-Infinity" minWidth="-Infinity" prefHeight="700.0" prefWidth="800.0" xmlns="http://javafx.com/javafx/8.0.172-ea" xmlns:fx="http://javafx.com/fxml/1" fx:controller="grahps.Controller">
<children>
<TextField fx:id="factorA" prefHeight="33.0" prefWidth="106.0" AnchorPane.bottomAnchor="75.0" AnchorPane.leftAnchor="24.0" AnchorPane.rightAnchor="670.0" promptText="a" />
<TextField fx:id="factorB" prefHeight="33.0" prefWidth="106.0" AnchorPane.bottomAnchor="37.0" AnchorPane.leftAnchor="24.0" AnchorPane.rightAnchor="670.0" promptText="b" />
<TextField fx:id="factorC" prefHeight="33.0" prefWidth="106.0" AnchorPane.bottomAnchor="1.0" AnchorPane.leftAnchor="24.0" AnchorPane.rightAnchor="670.0" promptText="c" />
<TextField fx:id="xMin" prefHeight="47.0" prefWidth="120.0" AnchorPane.bottomAnchor="61.0" AnchorPane.leftAnchor="158.0" AnchorPane.rightAnchor="522.0" promptText="xMin" />
<TextField fx:id="xMax" prefHeight="47.0" prefWidth="120.0" AnchorPane.bottomAnchor="3.0" AnchorPane.leftAnchor="158.0" AnchorPane.rightAnchor="522.0" promptText="xMax" />
<Label fx:id="label" prefHeight="61.0" prefWidth="276.0" AnchorPane.bottomAnchor="30.0" AnchorPane.leftAnchor="468.0" AnchorPane.rightAnchor="56.0" text="f(x)=" >
<font>
<Font size="18.0" />
</font>
</Label>
<LineChart fx:id="chart" prefHeight="598.0" prefWidth="800.0" title="Chart">
<xAxis>
<NumberAxis fx:id="xAxis" side="BOTTOM" />
</xAxis>
<yAxis>
<NumberAxis fx:id="yAxis" side="LEFT" />
</yAxis>
</LineChart>
<Button fx:id="button" prefHeight="61.0" prefWidth="98.0" layoutX="317.0" layoutY="612.0" mnemonicParsing="false" text="Rysuj wykres" onAction="#updateChart" />
</children>
</AnchorPane>
进行这些更改后,应用程序启动时将显示一个空图表(因为尚未实施任何初始化)。单击该按钮无效(因为尚未实施更新)。
初始化:
public void initialize(){
initChartProperties();
initInputControls();
XYChart.Series<Number, Number> series = getSeries();
chart.getData().add(series);
}
getSeries
-方法本质上包含drawChart
-方法的逻辑:
private XYChart.Series<Number, Number> getSeries() {
double xMax1 = Double.parseDouble(xMax.getText());
double xMin1 = Double.parseDouble(xMin.getText());
double a = Double.parseDouble(factorA.getText());
double b = Double.parseDouble(factorB.getText());
double c = Double.parseDouble(factorC.getText());
XYChart.Series<Number,Number> series = new XYChart.Series<Number, Number>();
series.setName("Chart");
String pattern;
if (a == 0 && c == 0) {
pattern = "f(x)=" + factorB.getText();
label.setText(pattern);
} else if (c == 0) {
pattern = "f(x)=" + factorA.getText() + "x+" + factorB.getText();
label.setText(pattern);
for (double i = xMin1; i <= xMax1; i++) {
double y = a * i + b;
series.getData().add(new Data<Number, Number>(i, y));
}
} else {
pattern = "f(x)=" + factorA.getText() + "x^2+" + factorB.getText() + "x+" + factorC.getText();
label.setText(pattern);
for (double i = xMin1; i < xMax1; i++) {
double y = a * i * i + b * i + c;
series.getData().add(new Data<Number, Number>(i, y));
}
}
return series;
}
initInputControls
-方法初始化输入控件,例如:
private void initInputControls() {
xMax.setText("100.0");
xMin.setText("10.0");
factorA.setText("1.0");
factorB.setText("2.0");
factorC.setText("3.0");
}
initChartProperties
-方法初始化图表:
private void initChartProperties() {
chart.setAnimated(true);
xAxis.setLabel("X Label");
yAxis.setLabel("Y Label");
}
如果要在启动时显示空图表,只需删除 initialize
-方法中的最后三行。
更新:更新只是简单地删除旧系列并将新系列添加到图表中:
@FXML
public void updateChart() {
XYChart.Series<Number, Number> series = getSeries();
chart.getData().clear();
chart.getData().add(series);
}
进行这些更改后,应用程序的行为符合预期。左图为启动后的应用程序,右图为更新输入值后的应用程序。
有些事情仍然可以改进,例如如果更改 window 大小,则应用程序无法正确缩放。此外,缺少输入字段的验证。可以在 initChartProperties
方法中禁用图表动画。
更新:
如果不显示任何符号,在initChartProperties
-方法中添加:
chart.setCreateSymbols(false);
结果是:
我的程序有问题。 我想在提供数据(方程的系数)后绘制图表。
我尝试更改我的导入(它对一些变量有帮助) 我将 java.awt* 更改为 javafx.scene ...(很少导入)。
FXML 文件:
<?xml version="1.0" encoding="UTF-8"?>
<?import javafx.scene.chart.CategoryAxis?>
<?import javafx.scene.chart.LineChart?>
<?import javafx.scene.chart.NumberAxis?>
<?import javafx.scene.control.Button?>
<?import javafx.scene.control.Label?>
<?import javafx.scene.control.TextField?>
<?import javafx.scene.layout.AnchorPane?>
<?import javafx.scene.text.Font?>
<AnchorPane maxHeight="-Infinity" maxWidth="-Infinity" minHeight="-Infinity" minWidth="-Infinity" prefHeight="700.0" prefWidth="800.0" xmlns="http://javafx.com/javafx/8.0.172-ea" xmlns:fx="http://javafx.com/fxml/1" fx:controller="grahps.Controller">
<children>
<TextField fx:id="factorA" layoutX="24.0" layoutY="598.0" prefHeight="33.0" prefWidth="106.0" text="a=" AnchorPane.bottomAnchor="75.0" AnchorPane.leftAnchor="24.0" AnchorPane.rightAnchor="670.0" />
<TextField fx:id="factorB" layoutX="24.0" layoutY="630.0" prefHeight="33.0" prefWidth="106.0" text="b=" AnchorPane.bottomAnchor="37.0" AnchorPane.leftAnchor="24.0" AnchorPane.rightAnchor="670.0" />
<TextField fx:id="factorC" layoutX="24.0" layoutY="674.0" prefHeight="33.0" prefWidth="106.0" text="c=" AnchorPane.bottomAnchor="1.0" AnchorPane.leftAnchor="24.0" AnchorPane.rightAnchor="670.0" />
<TextField layoutX="158.0" layoutY="592.0" prefHeight="47.0" prefWidth="120.0" text="xMin=" AnchorPane.bottomAnchor="61.0" AnchorPane.leftAnchor="158.0" AnchorPane.rightAnchor="522.0" fx:id="xMin" />
<TextField layoutX="158.0" layoutY="650.0" prefHeight="47.0" prefWidth="120.0" text="xMax=" AnchorPane.bottomAnchor="3.0" AnchorPane.leftAnchor="158.0" AnchorPane.rightAnchor="522.0" fx:id="xMax" />
<Label fx:id="label" layoutX="468.0" layoutY="629.0" prefHeight="61.0" prefWidth="276.0" text="f(x)=" AnchorPane.bottomAnchor="30.0" AnchorPane.leftAnchor="468.0" AnchorPane.rightAnchor="56.0">
<font>
<Font size="18.0" />
</font>
</Label>
<LineChart fx:id="drawChart" prefHeight="598.0" prefWidth="800.0" title="Chart">
<xAxis>
<CategoryAxis side="BOTTOM" />
</xAxis>
<yAxis>
<NumberAxis side="LEFT" />
</yAxis>
</LineChart>
<Button fx:id="button" layoutX="317.0" layoutY="612.0" mnemonicParsing="false" prefHeight="61.0" prefWidth="98.0" text="Rysuj wykres" />
</children>
</AnchorPane>
带有折线图(fx:id="drawChart")的部分生成的通信:“未解决fx:id参考
我的主要class:
package grahps;
import javafx.application.Application;
import javafx.fxml.FXMLLoader;
import javafx.scene.Parent;
import javafx.scene.Scene;
import javafx.stage.Stage;
public class Main extends Application {
private Controller controller;//
@Override
public void start(Stage stage) throws Exception {
stage.show();
System.out.println(getClass().getResource("/fxml/sample.fxml").getPath());
FXMLLoader loader = new FXMLLoader(getClass().getResource("/fxml/sample.fxml"));
loader.setController(controller);
Parent root = FXMLLoader.load(getClass().getResource("/fxml/sample.fxml"));
Scene scene = new Scene(root, 800, 800);
controller.drawChart(stage);
stage.setScene(scene);
}
public static void main(String[] args) {
launch(args);
}
}
最后是我的控制器:
package grahps;
import javafx.fxml.FXML;
import javafx.scene.Scene;
import javafx.stage.Stage;
import javafx.scene.control.TextField;
import javafx.scene.control.Label;
import javafx.scene.control.Button;
import javafx.scene.chart.NumberAxis;
import javafx.scene.chart.XYChart;
public class Controller {
@FXML
TextField factorA;
@FXML
TextField factorB;
@FXML
TextField factorC;
@FXML
TextField xMin;
@FXML
TextField xMax;
@FXML
Label label;
@FXML
Button button;
@FXML
XYChart.Series<Number, Number> chart;
//Parser Text Field -> double
double xMax1 = Double.parseDouble(xMax.getText());
double xMin1 = Double.parseDouble(xMin.getText());
double a = Double.parseDouble(factorA.getText());
double b = Double.parseDouble(factorB.getText());
double c = Double.parseDouble(factorC.getText());
@FXML
public void drawChart(Stage stage) {
XYChart.Series<Number, Number> series = chart;
series.setName("Chart");
final NumberAxis xAxis = new NumberAxis(xMin1, xMax1, 1);
final NumberAxis yAxis = new NumberAxis();
yAxis.setTickUnit(1);
xAxis.setLabel("X Label");
yAxis.setLabel("Y Label");
final javafx.scene.chart.LineChart<Number, Number> lineChart = new javafx.scene.chart.LineChart<Number, Number>(xAxis, yAxis);
double y;
String pattern;
if (a == 0 && c == 0) {
pattern = "f(x)=" + factorB;
label.setText(pattern);
} else if (c == 0) {
pattern = "f(x)=" + factorA + "x+" + factorB;
label.setText(pattern);
for (double i = xMin1; i <= xMax1; i++) {
y = a * i + b;
series.getData().add(new XYChart.Data(i, y));
}
} else {
pattern = "f(x)=" + factorC + "x^2" + factorA + "x+" + factorB;
label.setText(pattern);
for (double i = xMin1; i < xMax1; i++) {
y = a * i * i + b * i + c;
series.getData().add(new XYChart.Data(i, y));
}
}
lineChart.getData().add(series);
Scene scene = new Scene(lineChart, 800, 800);
stage.setScene(scene);
stage.setResizable(false);
stage.show();
}
}
请注意一件事:这是我的第一个 JavaFX 项目。 简单的我想填入方程的系数,点击按钮后生成方程模式+画图表。
非常感谢您的帮助。
当我删除上述方法和编译代码的@FXML 注释时,出现了这些错误:
Exception in Application start method
java.lang.reflect.InvocationTargetException
at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
at sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:62)
at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43)
at java.lang.reflect.Method.invoke(Method.java:498)
at com.sun.javafx.application.LauncherImpl.launchApplicationWithArgs(LauncherImpl.java:389)
at com.sun.javafx.application.LauncherImpl.launchApplication(LauncherImpl.java:328)
at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
at sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:62)
at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43)
at java.lang.reflect.Method.invoke(Method.java:498)
at sun.launcher.LauncherHelper$FXHelper.main(LauncherHelper.java:767)
Caused by: java.lang.RuntimeException: Exception in Application start method
at com.sun.javafx.application.LauncherImpl.launchApplication1(LauncherImpl.java:917)
at com.sun.javafx.application.LauncherImpl.lambda$launchApplication4(LauncherImpl.java:182)
at java.lang.Thread.run(Thread.java:748)
Caused by: javafx.fxml.LoadException:
/C:/Users/Damian/IdeaProjects/Graphs/target/classes/fxml/sample.fxml:14
at javafx.fxml.FXMLLoader.constructLoadException(FXMLLoader.java:2601)
at javafx.fxml.FXMLLoader.loadImpl(FXMLLoader.java:2579)
at javafx.fxml.FXMLLoader.loadImpl(FXMLLoader.java:2441)
at javafx.fxml.FXMLLoader.loadImpl(FXMLLoader.java:3214)
at javafx.fxml.FXMLLoader.loadImpl(FXMLLoader.java:3175)
at javafx.fxml.FXMLLoader.loadImpl(FXMLLoader.java:3148)
at javafx.fxml.FXMLLoader.loadImpl(FXMLLoader.java:3124)
at javafx.fxml.FXMLLoader.loadImpl(FXMLLoader.java:3104)
at javafx.fxml.FXMLLoader.load(FXMLLoader.java:3097)
at grahps.Main.start(Main.java:22)
at com.sun.javafx.application.LauncherImpl.lambda$launchApplication11(LauncherImpl.java:863)
at com.sun.javafx.application.PlatformImpl.lambda$runAndWait4(PlatformImpl.java:326)
at com.sun.javafx.application.PlatformImpl.lambda$null2(PlatformImpl.java:295)
at java.security.AccessController.doPrivileged(Native Method)
at com.sun.javafx.application.PlatformImpl.lambda$runLater3(PlatformImpl.java:294)
at com.sun.glass.ui.InvokeLaterDispatcher$Future.run(InvokeLaterDispatcher.java:95)
at com.sun.glass.ui.win.WinApplication._runLoop(Native Method)
at com.sun.glass.ui.win.WinApplication.lambda$null7(WinApplication.java:177)
... 1 more
Caused by: java.lang.NullPointerException
at grahps.Controller.<init>(Controller.java:31)
at sun.reflect.NativeConstructorAccessorImpl.newInstance0(Native Method)
at sun.reflect.NativeConstructorAccessorImpl.newInstance(NativeConstructorAccessorImpl.java:62)
at sun.reflect.DelegatingConstructorAccessorImpl.newInstance(DelegatingConstructorAccessorImpl.java:45)
at java.lang.reflect.Constructor.newInstance(Constructor.java:423)
at java.lang.Class.newInstance(Class.java:442)
at sun.reflect.misc.ReflectUtil.newInstance(ReflectUtil.java:51)
at javafx.fxml.FXMLLoader$ValueElement.processAttribute(FXMLLoader.java:927)
at javafx.fxml.FXMLLoader$InstanceDeclarationElement.processAttribute(FXMLLoader.java:971)
at javafx.fxml.FXMLLoader$Element.processStartElement(FXMLLoader.java:220)
at javafx.fxml.FXMLLoader$ValueElement.processStartElement(FXMLLoader.java:744)
at javafx.fxml.FXMLLoader.processStartElement(FXMLLoader.java:2707)
at javafx.fxml.FXMLLoader.loadImpl(FXMLLoader.java:2527)
... 17 more
Exception running application grahps.Main
Process finished with exit code 1
另外,我将 onAction="drawChart" 更改为 onAction="chart",现在我可以看到突出显示的通信:"cannot set javafx.scene.control.LineChart to chart"
代码中有几个错误,例如:
控制器中的
//Parser Text Field -> double
块执行得太早=>NullPointerException
.在
start
方法中调用controller.drawChart(stage)
时controller
等于null
=>NullPointerException
.控制器中的
drawChart
方法中series
等于null
,因为chart
等于null
=>NullPointerException
.控制器中缺少对图表的引用。这一点已经在评论中提到了。
CategoryAxis
作为x轴的类型,虽然x数据是数值。
在修复这些错误之前,应该改进架构(这将自动修复一些错误):
Controller
-class:由于每次单击按钮都必须初始化图表,然后更新图表,因此控制器中的以下更改将很有用:实现一个
initialize
方法,可以在其中进行必要的初始化。实现一个
updateChart
方法,该方法在单击按钮时调用并更新图表。定义对折线图的引用。
定义对两个轴的引用。
因此,
Controller
-class 看起来如下:public class Controller { @FXML TextField factorA; @FXML TextField factorB; @FXML TextField factorC; @FXML TextField xMin; @FXML TextField xMax; @FXML Label label; @FXML Button button; @FXML LineChart<Number, Number> chart; @FXML NumberAxis xAxis; @FXML NumberAxis yAxis; @FXML public void updateChart() {/*ToDo*/} public void initialize(){/*ToDo*/} }
Main
-class:在start
方法中只需要加载FXML:@Override public void start(Stage stage) throws Exception { Parent root = FXMLLoader.load(getClass().getResource("/fxml/sample.fxml")); Scene scene = new Scene(root, 800, 800); stage.setScene(scene); stage.show(); }
FXML:应进行以下更改:
- 将折线图的ID改为
fx:id="chart"
- 将折线图的x轴类型改为
NumberAxis
- 将
onAction="#updateChart"
添加到按钮。这会在单击按钮时调用updateChart
方法。 - 为两个轴定义一个 ID(
fx:id="xAxis"
和fx:id="yAxis"
)。 - 去除所有文本字段的所有非数字字符初始化(例如
text="a="
),否则会出现解析问题(使用标签或水印更有意义,例如promptText="a"
) .
然后,FXML 变为:
<AnchorPane maxHeight="-Infinity" maxWidth="-Infinity" minHeight="-Infinity" minWidth="-Infinity" prefHeight="700.0" prefWidth="800.0" xmlns="http://javafx.com/javafx/8.0.172-ea" xmlns:fx="http://javafx.com/fxml/1" fx:controller="grahps.Controller"> <children> <TextField fx:id="factorA" prefHeight="33.0" prefWidth="106.0" AnchorPane.bottomAnchor="75.0" AnchorPane.leftAnchor="24.0" AnchorPane.rightAnchor="670.0" promptText="a" /> <TextField fx:id="factorB" prefHeight="33.0" prefWidth="106.0" AnchorPane.bottomAnchor="37.0" AnchorPane.leftAnchor="24.0" AnchorPane.rightAnchor="670.0" promptText="b" /> <TextField fx:id="factorC" prefHeight="33.0" prefWidth="106.0" AnchorPane.bottomAnchor="1.0" AnchorPane.leftAnchor="24.0" AnchorPane.rightAnchor="670.0" promptText="c" /> <TextField fx:id="xMin" prefHeight="47.0" prefWidth="120.0" AnchorPane.bottomAnchor="61.0" AnchorPane.leftAnchor="158.0" AnchorPane.rightAnchor="522.0" promptText="xMin" /> <TextField fx:id="xMax" prefHeight="47.0" prefWidth="120.0" AnchorPane.bottomAnchor="3.0" AnchorPane.leftAnchor="158.0" AnchorPane.rightAnchor="522.0" promptText="xMax" /> <Label fx:id="label" prefHeight="61.0" prefWidth="276.0" AnchorPane.bottomAnchor="30.0" AnchorPane.leftAnchor="468.0" AnchorPane.rightAnchor="56.0" text="f(x)=" > <font> <Font size="18.0" /> </font> </Label> <LineChart fx:id="chart" prefHeight="598.0" prefWidth="800.0" title="Chart"> <xAxis> <NumberAxis fx:id="xAxis" side="BOTTOM" /> </xAxis> <yAxis> <NumberAxis fx:id="yAxis" side="LEFT" /> </yAxis> </LineChart> <Button fx:id="button" prefHeight="61.0" prefWidth="98.0" layoutX="317.0" layoutY="612.0" mnemonicParsing="false" text="Rysuj wykres" onAction="#updateChart" /> </children> </AnchorPane>
- 将折线图的ID改为
进行这些更改后,应用程序启动时将显示一个空图表(因为尚未实施任何初始化)。单击该按钮无效(因为尚未实施更新)。
初始化:
public void initialize(){ initChartProperties(); initInputControls(); XYChart.Series<Number, Number> series = getSeries(); chart.getData().add(series); }
getSeries
-方法本质上包含drawChart
-方法的逻辑:private XYChart.Series<Number, Number> getSeries() { double xMax1 = Double.parseDouble(xMax.getText()); double xMin1 = Double.parseDouble(xMin.getText()); double a = Double.parseDouble(factorA.getText()); double b = Double.parseDouble(factorB.getText()); double c = Double.parseDouble(factorC.getText()); XYChart.Series<Number,Number> series = new XYChart.Series<Number, Number>(); series.setName("Chart"); String pattern; if (a == 0 && c == 0) { pattern = "f(x)=" + factorB.getText(); label.setText(pattern); } else if (c == 0) { pattern = "f(x)=" + factorA.getText() + "x+" + factorB.getText(); label.setText(pattern); for (double i = xMin1; i <= xMax1; i++) { double y = a * i + b; series.getData().add(new Data<Number, Number>(i, y)); } } else { pattern = "f(x)=" + factorA.getText() + "x^2+" + factorB.getText() + "x+" + factorC.getText(); label.setText(pattern); for (double i = xMin1; i < xMax1; i++) { double y = a * i * i + b * i + c; series.getData().add(new Data<Number, Number>(i, y)); } } return series; }
initInputControls
-方法初始化输入控件,例如:private void initInputControls() { xMax.setText("100.0"); xMin.setText("10.0"); factorA.setText("1.0"); factorB.setText("2.0"); factorC.setText("3.0"); }
initChartProperties
-方法初始化图表:private void initChartProperties() { chart.setAnimated(true); xAxis.setLabel("X Label"); yAxis.setLabel("Y Label"); }
如果要在启动时显示空图表,只需删除
initialize
-方法中的最后三行。更新:更新只是简单地删除旧系列并将新系列添加到图表中:
@FXML public void updateChart() { XYChart.Series<Number, Number> series = getSeries(); chart.getData().clear(); chart.getData().add(series); }
进行这些更改后,应用程序的行为符合预期。左图为启动后的应用程序,右图为更新输入值后的应用程序。
有些事情仍然可以改进,例如如果更改 window 大小,则应用程序无法正确缩放。此外,缺少输入字段的验证。可以在 initChartProperties
方法中禁用图表动画。
更新:
如果不显示任何符号,在initChartProperties
-方法中添加:
chart.setCreateSymbols(false);
结果是: