JavaFX 访问控制器给出空指针异常
JavaFX accessing controller gives nullpointerexception
我想要实现的是从 main 方法获取控制器实例,这样我就可以从另一个 class 调用控制器的方法并更新 fxml。无论如何,这是我的代码:
主要class:
public class Main extends Application {
Controller controller;
@Override
public void start(Stage primaryStage) throws Exception{
FXMLLoader fxmlLoader = new FXMLLoader();
Parent root = fxmlLoader.load(getClass().getResource("sample.fxml"));
controller = fxmlLoader.getController();
primaryStage.setTitle("uPick Smart Service");
primaryStage.setScene(new Scene(root, 1600, 600));
primaryStage.show();
ConnectionHandling connectionHandling = new ConnectionHandling();
Thread X = new Thread (connectionHandling);
X.start();
}
public static void main(String args[]){
launch(args);
}
public Controller getController(){
return controller;
}
}
我的控制器class:
public class Controller {
public HBox billbox;
public int childnr;
public void createBill() {
System.out.println("Creating");
TableView<Item> bill = new TableView<>();
DropShadow dropShadow = new DropShadow();
dropShadow.setRadius(5.0);
dropShadow.setOffsetX(3.0);
dropShadow.setOffsetY(3.0);
dropShadow.setColor(Color.color(0.4, 0.5, 0.5));
VBox fullbill = new VBox();
fullbill.setPadding(new Insets(1, 1, 1, 1));
fullbill.getStyleClass().add("fullbill");
TableColumn<Item, String> nameColumn = new TableColumn<>("Emri");
nameColumn.setMinWidth(200);
nameColumn.setCellValueFactory(new PropertyValueFactory<>("name"));
TableColumn<Item, String> quantsColumn = new TableColumn<>("Sasia");
quantsColumn.setMinWidth(50);
quantsColumn.setCellValueFactory(new PropertyValueFactory<>("quants"));
double tablewidth = nameColumn.getWidth() + quantsColumn.getWidth();
Label tablenrlabel = new Label("Table 5");
tablenrlabel.getStyleClass().add("tablenr-label");
tablenrlabel.setMinWidth(tablewidth);
Button closebutton = new Button("Mbyll");
closebutton.setMinWidth(tablewidth);
closebutton.getStyleClass().add("red-tint");
bill.setItems(getItem());
bill.setMinWidth(256);
bill.setColumnResizePolicy(TableView.CONSTRAINED_RESIZE_POLICY);
bill.getColumns().addAll(nameColumn, quantsColumn);
fullbill.setEffect(dropShadow);
fullbill.getChildren().addAll(tablenrlabel, bill, closebutton);
billbox.getChildren().addAll(fullbill);
childnr += 1;
//Loops over every button in every vbox and gives it a seperate listener (the index of the button is hardcoded so it can cause problems if you add more items)
for (int i = 0; i < childnr; i++) {
VBox box = (VBox) billbox.getChildren().get(i);
Button btn = (Button) box.getChildren().get(2); //if sudden issues change this
btn.setId(Integer.valueOf(i).toString());
btn.setOnAction(new EventHandler<ActionEvent>() {
@Override
public void handle(ActionEvent event) {
int index = billbox.getChildren().indexOf(btn.getParent());
billbox.getChildren().remove(index);
childnr -= 1;
System.out.println(btn.getId());
}
});
}
System.out.println("done");
}
}
并且 class 试图调用控制器方法:
public class TakeOrder implements Runnable {
Socket SOCK;
Controller controller;
//OrderIndexes: "Order","Waiter","Payment"
private int NonConnectedDatas = 2;
public TakeOrder(Socket X){
this.SOCK = X;
}
public void CheckConnection() throws IOException{
System.out.println("Checking connection");
if(!SOCK.isConnected()){
System.out.println("Dissconectiong");
for(int i = 0; i < ConnectionHandling.ConnectionArray.size(); i++){
if(ConnectionHandling.ConnectionArray.get(i) == SOCK){
ConnectionHandling.ConnectionArray.remove(i);
}
}
}
}
public void run(){
try{
try{
CheckConnection();
ObjectInputStream ob = new ObjectInputStream(SOCK.getInputStream());
String[] structuredArray = (String[])ob.readObject();
String tablenr = structuredArray[0];
String index = structuredArray[1];
ArrayList<String> names = new ArrayList<>();
ArrayList<String> quants = new ArrayList<>();
int a = 0;
int b = 0;
switch (index) {
case "Order":
for (int i = NonConnectedDatas; i < structuredArray.length; i++) {
if (i % 2 == 0) {
names.add(a, structuredArray[i]);
System.out.println(names.get(a));
a++;
} else {
quants.add(b, structuredArray[i]);
System.out.println(quants.get(b));
b++;
}
}
break;
}
Platform.runLater(new Runnable() {
@Override
public void run() {
}
});
}finally{
SOCK.close();
}
}catch(Exception X){
System.out.print(X);
}
}
}
这是我的错误信息:
Exception in thread "JavaFX Application Thread" java.lang.NullPointerException
at sample.TakeOrder.run(TakeOrder.java:80)
at com.sun.javafx.application.PlatformImpl.lambda$null3(PlatformImpl.java:295)
at java.security.AccessController.doPrivileged(Native Method)
at com.sun.javafx.application.PlatformImpl.lambda$runLater4(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$null8(WinApplication.java:191)
at java.lang.Thread.run(Thread.java:745)
ConnectionHandling class:
public class ConnectionHandling implements Runnable{
public static ArrayList<Socket> ConnectionArray = new ArrayList<Socket>();
public void run(){
System.out.println("Starting");
try{
final int PORT = 60123;
ServerSocket SERVER = new ServerSocket(PORT);
System.out.println("Waiting for clients");
while(true){
Socket SOCK = SERVER.accept();
ConnectionArray.add(SOCK);
TakeOrder ORDER = new TakeOrder(SOCK);
Thread X = new Thread(ORDER);
X.setDaemon(true);
X.start();
}
}catch(Exception x){
System.out.print(x);
}
}
首先,您使用的是static
FXMLLoader.load(URL)
方法。因为它是 static
,您创建的 FXMLLoader
实例的控制器 属性 未通过调用此 load
方法进行初始化。所以 Main
中的 controller
将是 null
.
相反,设置位置并使用实例方法load()
:
@Override
public void start(Stage primaryStage) throws Exception{
FXMLLoader fxmlLoader = new FXMLLoader();
fxmlLoader.setLocation(getClass().getResource("sample.fxml"));
Parent root = fxmlLoader.load();
controller = fxmlLoader.getController();
primaryStage.setTitle("uPick Smart Service");
primaryStage.setScene(new Scene(root, 1600, 600));
primaryStage.show();
}
不过,这并不能完全解决您的问题。当您启动该应用程序时,JavaFX 将创建一个 Main
实例并在该实例上调用 start()
。通过上述更改,该实例 的 controller
字段将被正确初始化。
但是,在 TakeOrder.run()
中,您创建了 Main
的另一个实例:
Main main = new Main();
该实例的 controller
字段不会被初始化(即使您初始化了它,它也与您想要的实例不同)。所以你真的需要安排 TakeOrder
来访问在 start 方法中创建的 controller 实例 。
下面是对您的代码进行最直接的修复以使其正常工作:
public class ConnectionHandling implements Runnable{
private final Controller controller ;
public ConnectionHandling(Controller controller) {
this.controller = controller ;
}
// ...
public void run(){
// existing code ...
TakeOrder ORDER = new TakeOrder(SOCK, connection);
// ...
}
}
和
public class TakeOrder implements Runnable {
Socket SOCK;
Controller controller;
//OrderIndexes: "Order","Waiter","Payment"
private int NonConnectedDatas = 2;
public TakeOrder(Socket X, Controller controller){
this.SOCK = X;
this.controller = controller ;
}
// ...
public void run() {
// ...
Platform.runLater(controller::createBill);
// ...
}
}
最后
public class Main extends Application {
@Override
public void start(Stage primaryStage) throws Exception{
FXMLLoader fxmlLoader = new FXMLLoader();
Parent root = fxmlLoader.load(getClass().getResource("sample.fxml"));
Controller controller = fxmlLoader.getController();
primaryStage.setTitle("uPick Smart Service");
primaryStage.setScene(new Scene(root, 1600, 600));
primaryStage.show();
ConnectionHandling connectionHandling = new ConnectionHandling(controller);
Thread X = new Thread (connectionHandling);
X.start();
}
public static void main(String args[]){
launch(args);
}
public Controller getController(){
return controller;
}
}
一般来说,对于这样的应用程序,您可能需要考虑使用 MVC 或 MVP 方法(即您需要一个 模型 class,这将适用于服务,例如您的 ConnectionHandler
).
您可能还对 this article 在 JavaFX 中集成服务感兴趣。
如果您手动实例化 Application
(在本例中为 Main
class),则不会调用 start()
函数。尝试使用
启动您的应用程序
Application.launch
而不是
Main main = new Main()
此外,launch
方法在应用程序退出之前不会 return。因此,您可能希望对 getController()
的调用重新排序
还有另一种管理控制器实例的方法,那就是使用 FXMLLoader 中的 setController(Object controller)
。
文档:
我想要实现的是从 main 方法获取控制器实例,这样我就可以从另一个 class 调用控制器的方法并更新 fxml。无论如何,这是我的代码:
主要class:
public class Main extends Application {
Controller controller;
@Override
public void start(Stage primaryStage) throws Exception{
FXMLLoader fxmlLoader = new FXMLLoader();
Parent root = fxmlLoader.load(getClass().getResource("sample.fxml"));
controller = fxmlLoader.getController();
primaryStage.setTitle("uPick Smart Service");
primaryStage.setScene(new Scene(root, 1600, 600));
primaryStage.show();
ConnectionHandling connectionHandling = new ConnectionHandling();
Thread X = new Thread (connectionHandling);
X.start();
}
public static void main(String args[]){
launch(args);
}
public Controller getController(){
return controller;
}
}
我的控制器class:
public class Controller {
public HBox billbox;
public int childnr;
public void createBill() {
System.out.println("Creating");
TableView<Item> bill = new TableView<>();
DropShadow dropShadow = new DropShadow();
dropShadow.setRadius(5.0);
dropShadow.setOffsetX(3.0);
dropShadow.setOffsetY(3.0);
dropShadow.setColor(Color.color(0.4, 0.5, 0.5));
VBox fullbill = new VBox();
fullbill.setPadding(new Insets(1, 1, 1, 1));
fullbill.getStyleClass().add("fullbill");
TableColumn<Item, String> nameColumn = new TableColumn<>("Emri");
nameColumn.setMinWidth(200);
nameColumn.setCellValueFactory(new PropertyValueFactory<>("name"));
TableColumn<Item, String> quantsColumn = new TableColumn<>("Sasia");
quantsColumn.setMinWidth(50);
quantsColumn.setCellValueFactory(new PropertyValueFactory<>("quants"));
double tablewidth = nameColumn.getWidth() + quantsColumn.getWidth();
Label tablenrlabel = new Label("Table 5");
tablenrlabel.getStyleClass().add("tablenr-label");
tablenrlabel.setMinWidth(tablewidth);
Button closebutton = new Button("Mbyll");
closebutton.setMinWidth(tablewidth);
closebutton.getStyleClass().add("red-tint");
bill.setItems(getItem());
bill.setMinWidth(256);
bill.setColumnResizePolicy(TableView.CONSTRAINED_RESIZE_POLICY);
bill.getColumns().addAll(nameColumn, quantsColumn);
fullbill.setEffect(dropShadow);
fullbill.getChildren().addAll(tablenrlabel, bill, closebutton);
billbox.getChildren().addAll(fullbill);
childnr += 1;
//Loops over every button in every vbox and gives it a seperate listener (the index of the button is hardcoded so it can cause problems if you add more items)
for (int i = 0; i < childnr; i++) {
VBox box = (VBox) billbox.getChildren().get(i);
Button btn = (Button) box.getChildren().get(2); //if sudden issues change this
btn.setId(Integer.valueOf(i).toString());
btn.setOnAction(new EventHandler<ActionEvent>() {
@Override
public void handle(ActionEvent event) {
int index = billbox.getChildren().indexOf(btn.getParent());
billbox.getChildren().remove(index);
childnr -= 1;
System.out.println(btn.getId());
}
});
}
System.out.println("done");
}
}
并且 class 试图调用控制器方法:
public class TakeOrder implements Runnable {
Socket SOCK;
Controller controller;
//OrderIndexes: "Order","Waiter","Payment"
private int NonConnectedDatas = 2;
public TakeOrder(Socket X){
this.SOCK = X;
}
public void CheckConnection() throws IOException{
System.out.println("Checking connection");
if(!SOCK.isConnected()){
System.out.println("Dissconectiong");
for(int i = 0; i < ConnectionHandling.ConnectionArray.size(); i++){
if(ConnectionHandling.ConnectionArray.get(i) == SOCK){
ConnectionHandling.ConnectionArray.remove(i);
}
}
}
}
public void run(){
try{
try{
CheckConnection();
ObjectInputStream ob = new ObjectInputStream(SOCK.getInputStream());
String[] structuredArray = (String[])ob.readObject();
String tablenr = structuredArray[0];
String index = structuredArray[1];
ArrayList<String> names = new ArrayList<>();
ArrayList<String> quants = new ArrayList<>();
int a = 0;
int b = 0;
switch (index) {
case "Order":
for (int i = NonConnectedDatas; i < structuredArray.length; i++) {
if (i % 2 == 0) {
names.add(a, structuredArray[i]);
System.out.println(names.get(a));
a++;
} else {
quants.add(b, structuredArray[i]);
System.out.println(quants.get(b));
b++;
}
}
break;
}
Platform.runLater(new Runnable() {
@Override
public void run() {
}
});
}finally{
SOCK.close();
}
}catch(Exception X){
System.out.print(X);
}
}
}
这是我的错误信息:
Exception in thread "JavaFX Application Thread" java.lang.NullPointerException
at sample.TakeOrder.run(TakeOrder.java:80)
at com.sun.javafx.application.PlatformImpl.lambda$null3(PlatformImpl.java:295)
at java.security.AccessController.doPrivileged(Native Method)
at com.sun.javafx.application.PlatformImpl.lambda$runLater4(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$null8(WinApplication.java:191)
at java.lang.Thread.run(Thread.java:745)
ConnectionHandling class:
public class ConnectionHandling implements Runnable{
public static ArrayList<Socket> ConnectionArray = new ArrayList<Socket>();
public void run(){
System.out.println("Starting");
try{
final int PORT = 60123;
ServerSocket SERVER = new ServerSocket(PORT);
System.out.println("Waiting for clients");
while(true){
Socket SOCK = SERVER.accept();
ConnectionArray.add(SOCK);
TakeOrder ORDER = new TakeOrder(SOCK);
Thread X = new Thread(ORDER);
X.setDaemon(true);
X.start();
}
}catch(Exception x){
System.out.print(x);
}
}
首先,您使用的是static
FXMLLoader.load(URL)
方法。因为它是 static
,您创建的 FXMLLoader
实例的控制器 属性 未通过调用此 load
方法进行初始化。所以 Main
中的 controller
将是 null
.
相反,设置位置并使用实例方法load()
:
@Override
public void start(Stage primaryStage) throws Exception{
FXMLLoader fxmlLoader = new FXMLLoader();
fxmlLoader.setLocation(getClass().getResource("sample.fxml"));
Parent root = fxmlLoader.load();
controller = fxmlLoader.getController();
primaryStage.setTitle("uPick Smart Service");
primaryStage.setScene(new Scene(root, 1600, 600));
primaryStage.show();
}
不过,这并不能完全解决您的问题。当您启动该应用程序时,JavaFX 将创建一个 Main
实例并在该实例上调用 start()
。通过上述更改,该实例 的 controller
字段将被正确初始化。
但是,在 TakeOrder.run()
中,您创建了 Main
的另一个实例:
Main main = new Main();
该实例的 controller
字段不会被初始化(即使您初始化了它,它也与您想要的实例不同)。所以你真的需要安排 TakeOrder
来访问在 start 方法中创建的 controller 实例 。
下面是对您的代码进行最直接的修复以使其正常工作:
public class ConnectionHandling implements Runnable{
private final Controller controller ;
public ConnectionHandling(Controller controller) {
this.controller = controller ;
}
// ...
public void run(){
// existing code ...
TakeOrder ORDER = new TakeOrder(SOCK, connection);
// ...
}
}
和
public class TakeOrder implements Runnable {
Socket SOCK;
Controller controller;
//OrderIndexes: "Order","Waiter","Payment"
private int NonConnectedDatas = 2;
public TakeOrder(Socket X, Controller controller){
this.SOCK = X;
this.controller = controller ;
}
// ...
public void run() {
// ...
Platform.runLater(controller::createBill);
// ...
}
}
最后
public class Main extends Application {
@Override
public void start(Stage primaryStage) throws Exception{
FXMLLoader fxmlLoader = new FXMLLoader();
Parent root = fxmlLoader.load(getClass().getResource("sample.fxml"));
Controller controller = fxmlLoader.getController();
primaryStage.setTitle("uPick Smart Service");
primaryStage.setScene(new Scene(root, 1600, 600));
primaryStage.show();
ConnectionHandling connectionHandling = new ConnectionHandling(controller);
Thread X = new Thread (connectionHandling);
X.start();
}
public static void main(String args[]){
launch(args);
}
public Controller getController(){
return controller;
}
}
一般来说,对于这样的应用程序,您可能需要考虑使用 MVC 或 MVP 方法(即您需要一个 模型 class,这将适用于服务,例如您的 ConnectionHandler
).
您可能还对 this article 在 JavaFX 中集成服务感兴趣。
如果您手动实例化 Application
(在本例中为 Main
class),则不会调用 start()
函数。尝试使用
Application.launch
而不是
Main main = new Main()
此外,launch
方法在应用程序退出之前不会 return。因此,您可能希望对 getController()
还有另一种管理控制器实例的方法,那就是使用 FXMLLoader 中的 setController(Object controller)
。
文档: