在 VBox 上调用方法会抛出 NullPointerException
Calling methods on VBox throws NullPointerException
我正在开发客户端-服务器信使应用程序进行练习。在我的客户端模块中有 Client class、ClientController class、ClientGUI class 和 fxml 文件、Message class、CSS 文件和主要class。我的 ClientGUI class 和 ClientController class 中有一个方法,display(String message) 将 Message 对象添加到 ID 为#messages.
的 VBox
这是我的问题:在我的 ClientController class 中有一个调用 ClientGUI 的 display() 方法的 display() 方法(我知道这似乎是多余的,但这不是问题所在)。当在我的 ClientGUI class 中调用 display() 方法时,例如在 setOnMouseClick() 方法中,它工作正常。但是,当从我的 ClientController class 调用此方法时,我在 ClientGUI class 的 display() 方法中得到一个 NullPointerException 指向我的 VBox 类型的消息变量。我尝试用 VBox test = messages;
替换 messages.getChildren().add(new Message(message));
后者工作正常,而前者抛出异常。我还尝试在我的 messages 变量上调用其他方法,所有这些方法都抛出相同的错误。为什么会这样?
(仍处于开发过程中,因此可能存在其他不相关的问题)
客户:
package client;
import java.io.*;
import java.net.Socket;
/**
* Gabe Castelli
* 9/26/2016
* Description: Represents client and its socket
*/
public class Client implements Serializable {
private static Client instance;
private Socket clientSocket;
private BufferedReader in;
private PrintWriter out;
private ObjectOutputStream objectOutputStream;
private boolean connected = false;
private static final long serialVersionUID = 0L;
private Client() {}
public void connect(String targetAddress, String username_, int port_) {
try {
clientSocket = new Socket(targetAddress, port_);
clientSocket.setReuseAddress(true); // Allows same port to be used successively without cool-down
in = new BufferedReader(new InputStreamReader(clientSocket.getInputStream()));
out = new PrintWriter(clientSocket.getOutputStream(), true);
objectOutputStream = new ObjectOutputStream(clientSocket.getOutputStream());
objectOutputStream.writeObject(username_);
ClientController.getInstance().display("Attention: Successfully connected to server");
connected = true;
} catch (IOException e) {
ClientController.getInstance().display("Attention: Unable to connect to server");
}
}
public void disconnect() {
try {
if (in != null) {
in.close();
}
if (out != null) {
out.close();
}
if (objectOutputStream != null) {
objectOutputStream.close();
}
if (clientSocket != null) {
clientSocket.close();
}
} catch (IOException e) {
e.printStackTrace();
}
connected = false;
}
public boolean connected() {
return connected;
}
public static Client getInstance() {
if (instance == null) {
instance = new Client();
}
return instance;
}
public void sendMessage(String message) {
out.println(message);
}
}
客户端控制器:
package client;
import javafx.scene.layout.VBox;
/**
* Gabe Castelli
* 9/28/2016
* Description:
*/
public class ClientController implements Controller {
private static ClientController instance;
private ClientController() {}
public static ClientController getInstance() {
if (instance == null) {
instance = new ClientController();
}
return instance;
}
// Controls
public void display(String message) {
ClientGUI.getInstance().display(message);
}
public void displayAndSend(String message) {
display(message);
Client.getInstance().sendMessage(message);
}
public void connect(String address_, String username_, int port_) {
Client.getInstance().connect(address_, username_, port_);
}
public void disconnect() {
Client.getInstance().disconnect();
}
}
客户端图形用户界面:
package client;
import javafx.application.Application;
import javafx.fxml.FXMLLoader;
import javafx.geometry.Pos;
import javafx.scene.Parent;
import javafx.scene.Scene;
import javafx.scene.control.Button;
import javafx.scene.control.Label;
import javafx.scene.control.TextField;
import javafx.scene.input.KeyCode;
import javafx.scene.layout.VBox;
import javafx.stage.Stage;
/**
* Gabe Castelli
* 9/26/2016
* Description: GUI for client-side using JavaFX
*/
public class ClientGUI extends Application {
private Scene scene;
private TextField targetAddress;
private TextField fieldUsername;
private TextField fieldPort;
private VBox messages;
private TextField input;
private Button btnConnect;
private String username = "Anonymous";
private int port = 2000;
private static ClientGUI instance;
public static ClientGUI getInstance() {
if (instance == null) {
instance = new ClientGUI();
}
return instance;
}
@Override
public void start(Stage stage) throws Exception {
Parent root = FXMLLoader.load(getClass().getResource("ClientGUI.fxml"));
scene = new Scene(root, 480, 360);
stage.setTitle("MyMessenger");
stage.setScene(scene);
stage.setMinWidth(660);
stage.setMinHeight(495);
stage.setMaxWidth(990);
stage.show();
targetAddress = (TextField) scene.lookup("#targetAddress");
fieldUsername = (TextField) scene.lookup("#fieldUsername");
fieldPort = (TextField) scene.lookup("#fieldPort");
messages = (VBox) scene.lookup("#messages");
input = (TextField) scene.lookup("#input");
btnConnect = (Button) scene.lookup("#btnConnect");
input.setOnKeyPressed(event -> {
if (event.getCode() == KeyCode.ENTER) {
if (Client.getInstance().connected()) {
ClientController.getInstance().displayAndSend(input.getText());
input.setText("");
} else {
display("Attention: Not connected to server");
input.setText("");
}
}
});
btnConnect.setOnMouseClicked(event -> {
if (!Client.getInstance().connected()) {
if (!targetAddress.getText().equals("")) {
if (!fieldUsername.getText().equals("")) {
username = fieldUsername.getText();
}
if (!fieldPort.getText().equals("")) {
port = Integer.valueOf(fieldPort.getText());
}
ClientController.getInstance().connect(targetAddress.getText(), username, port);
btnConnect.setText("Disconnect");
} else {
display("Attention: Address required");
}
} else {
ClientController.getInstance().disconnect();
display("Attention: Disconnecting...");
btnConnect.setText("Connect");
}
});
}
public void display(String message) {
messages.getChildren().add(new Message(message));
}
}
堆栈跟踪:
Exception in thread "JavaFX Application Thread" java.lang.NullPointerException
at client.ClientGUI.display(ClientGUI.java:96)
at client.ClientController.display(ClientController.java:26)
at client.Client.connect(Client.java:34)
at client.ClientController.connect(ClientController.java:35)
at client.ClientGUI.lambda$start(ClientGUI.java:81)
at com.sun.javafx.event.CompositeEventHandler.dispatchBubblingEvent(CompositeEventHandler.java:86)
at com.sun.javafx.event.EventHandlerManager.dispatchBubblingEvent(EventHandlerManager.java:238)
at com.sun.javafx.event.EventHandlerManager.dispatchBubblingEvent(EventHandlerManager.java:191)
at com.sun.javafx.event.CompositeEventDispatcher.dispatchBubblingEvent(CompositeEventDispatcher.java:59)
at com.sun.javafx.event.BasicEventDispatcher.dispatchEvent(BasicEventDispatcher.java:58)
at com.sun.javafx.event.EventDispatchChainImpl.dispatchEvent(EventDispatchChainImpl.java:114)
at com.sun.javafx.event.BasicEventDispatcher.dispatchEvent(BasicEventDispatcher.java:56)
at com.sun.javafx.event.EventDispatchChainImpl.dispatchEvent(EventDispatchChainImpl.java:114)
at com.sun.javafx.event.EventUtil.fireEventImpl(EventUtil.java:74)
at com.sun.javafx.event.EventUtil.fireEvent(EventUtil.java:54)
at javafx.event.Event.fireEvent(Event.java:198)
at javafx.scene.Scene$ClickGenerator.postProcess(Scene.java:3470)
at javafx.scene.Scene$ClickGenerator.access00(Scene.java:3398)
at javafx.scene.Scene$MouseHandler.process(Scene.java:3766)
at javafx.scene.Scene$MouseHandler.access00(Scene.java:3485)
at javafx.scene.Scene.impl_processMouseEvent(Scene.java:1762)
at javafx.scene.Scene$ScenePeerListener.mouseEvent(Scene.java:2494)
at com.sun.javafx.tk.quantum.GlassViewEventHandler$MouseEventNotification.run(GlassViewEventHandler.java:352)
at com.sun.javafx.tk.quantum.GlassViewEventHandler$MouseEventNotification.run(GlassViewEventHandler.java:275)
at java.security.AccessController.doPrivileged(Native Method)
at com.sun.javafx.tk.quantum.GlassViewEventHandler.lambda$handleMouseEvent5(GlassViewEventHandler.java:388)
at com.sun.javafx.tk.quantum.QuantumToolkit.runWithoutRenderLock(QuantumToolkit.java:389)
at com.sun.javafx.tk.quantum.GlassViewEventHandler.handleMouseEvent(GlassViewEventHandler.java:387)
at com.sun.glass.ui.View.handleMouseEvent(View.java:555)
at com.sun.glass.ui.View.notifyMouse(View.java:937)
发生空指针异常是因为您从 ClientGUI.getInstance()
获得的 ClientGUI
实例不是作为 JavaFX 启动过程的一部分创建的实例,start()
方法被调用。由于 messages
在 start()
中初始化,您从 ClientGUI.getInstance()
获得的实例没有 messages
初始化。因此,当您调用 ClientGUI.getInstance().display(message)
时,它会调用 messages.getChildren()...
: messages
在 ClientGUI
实例中为空,您会得到一个空指针异常。
这里的根本问题是整体结构。你真的不能制作这些单例 - 特别是 Application
subclass - 因为它们是由框架实例化的。 ClientGUI
由 JavaFX 启动过程实例化,并且按照您使用 FXMLLoader
的方式,ClientController
由 FXMLLoader.load()
实例化(因此 ClientController.getInstance()
可能不是给你一个你希望它是的实例。
关于您为什么要尝试使这些 classes 成为单例,目前还不是很清楚,当然也没有任何意义。我的猜测是您正在尝试解决您现有的结构,这完全是非标准的。您定义的方法几乎都在 class 中,不应该对这些方法实现的功能负责。
特别是:Application
subclass 负责应用程序生命周期。它应该有一个 start
方法,可以简单地加载初始视图(FMXL 文件),将其放入 window 中并显示它。如果您 have/need 它们也可能会启动其他服务。如果需要,它可以选择覆盖 init()
和 stop()
。
控制器class负责更新视图。这是唯一应该引用 FXML 文件中定义的 UI 控件的地方,也是唯一应该修改显示的地方。
模型 class (Client
) 应该对视图或控制器一无所知。它应该只代表所呈现的状态或数据。根据模型表示的数据更新视图是控制器(实际上是一个演示者,在某些其他体系结构中这可以由视图完成)的责任。
因此,您可能希望按照以下几行重组应用程序。根据您可能需要的其他东西,这会有一些变化,但这至少应该让您走上可行的轨道:
客户端GUI:
public class ClientGUI extends Application {
@Override
public void start(Stage stage) throws Exception {
Parent root = FXMLLoader.load(getClass().getResource("ClientGUI.fxml"));
scene = new Scene(root, 480, 360);
stage.setTitle("MyMessenger");
stage.setScene(scene);
stage.setMinWidth(660);
stage.setMinHeight(495);
stage.setMaxWidth(990);
stage.show();
}
}
客户端控制器:
public class ClientController implements Controller {
@FXML
private TextField targetAddress;
@FXML
private TextField fieldUsername;
@FXML
private TextField fieldPort;
@FXML
private VBox messages;
@FXML
private TextField input;
@FXML
private Button btnConnect;
private String username = "Anonymous";
private int port = 2000;
private Client client ;
public void initialize() {
client = new Client();
}
@FXML
private void connect() {
if (!client.connected()) {
if (!targetAddress.getText().equals("")) {
if (!fieldUsername.getText().equals("")) {
username = fieldUsername.getText();
}
if (!fieldPort.getText().equals("")) {
port = Integer.valueOf(fieldPort.getText());
}
try {
client.connect(targetAddress.getText(), username, port);
btnConnect.setText("Disconnect");
display("Attention: Successfully connected to server");
} catch (IOException exc) {
display("Attention: Unable to connect to server");
}
} else {
display("Attention: Address required");
}
} else {
client.disconnect();
display("Attention: Disconnecting...");
btnConnect.setText("Connect");
}
}
@FXML
private void message() {
if (client.connected()) {
displayAndSend(input.getText());
input.setText("");
} else {
display("Attention: Not connected to server");
input.setText("");
}
}
public void display(String message) {
messages.getChildren().add(new Message(message));
}
public void displayAndSend(String message) {
display(message);
client.sendMessage(message);
}
}
然后是
// Note: why on earth is this Serializable? What state are you intending to serialize???
public class Client implements Serializable {
private Socket clientSocket;
private BufferedReader in;
private PrintWriter out;
private ObjectOutputStream objectOutputStream;
private boolean connected = false;
private static final long serialVersionUID = 0L;
public void connect(String targetAddress, String username_, int port_) throws IOException {
clientSocket = new Socket(targetAddress, port_);
clientSocket.setReuseAddress(true); // Allows same port to be used successively without cool-down
in = new BufferedReader(new InputStreamReader(clientSocket.getInputStream()));
out = new PrintWriter(clientSocket.getOutputStream(), true);
objectOutputStream = new ObjectOutputStream(clientSocket.getOutputStream());
objectOutputStream.writeObject(username_);
connected = true;
}
public void disconnect() {
try {
if (in != null) {
in.close();
}
if (out != null) {
out.close();
}
if (objectOutputStream != null) {
objectOutputStream.close();
}
if (clientSocket != null) {
clientSocket.close();
}
} catch (IOException e) {
e.printStackTrace();
}
connected = false;
}
public boolean connected() {
return connected;
}
public void sendMessage(String message) {
out.println(message);
}
}
要使控制器工作,您需要根元素定义一个 fx:controller="client.ClientController"
属性,并且 ClientGUI.fxml
中的控件定义与控制器中的字段名称匹配的 fx:id
属性class,并为要指定的事件处理程序方法,例如:
<!-- ... -->
<TextField fx:id="targetAddress" />
<TextField fx:id="fieldUserName" />
<TextField fx:id="fieldPort" />
<VBox fx:id="messages" />
<TextField fx:id="input" onAction="#message" />
<Button text="Connect" fx:id="btnConnect" onAction="#connect" />
<!-- ... -->
(请注意,当按下 enter 时,文本字段会触发 ActionEvent
,因此无需弄乱按键处理程序等)
您可能需要稍微修改一下以使应用程序的其余功能正常工作,但只要您坚持基本原则:只有控制器应该修改 UI; Client
class 应该不了解演示文稿的详细信息等,那么您就走对了。如果你做 anything static
,当然如果你试图做任何这些单例,你就做错了。
我正在开发客户端-服务器信使应用程序进行练习。在我的客户端模块中有 Client class、ClientController class、ClientGUI class 和 fxml 文件、Message class、CSS 文件和主要class。我的 ClientGUI class 和 ClientController class 中有一个方法,display(String message) 将 Message 对象添加到 ID 为#messages.
的 VBox这是我的问题:在我的 ClientController class 中有一个调用 ClientGUI 的 display() 方法的 display() 方法(我知道这似乎是多余的,但这不是问题所在)。当在我的 ClientGUI class 中调用 display() 方法时,例如在 setOnMouseClick() 方法中,它工作正常。但是,当从我的 ClientController class 调用此方法时,我在 ClientGUI class 的 display() 方法中得到一个 NullPointerException 指向我的 VBox 类型的消息变量。我尝试用 VBox test = messages;
messages.getChildren().add(new Message(message));
后者工作正常,而前者抛出异常。我还尝试在我的 messages 变量上调用其他方法,所有这些方法都抛出相同的错误。为什么会这样?
(仍处于开发过程中,因此可能存在其他不相关的问题)
客户:
package client;
import java.io.*;
import java.net.Socket;
/**
* Gabe Castelli
* 9/26/2016
* Description: Represents client and its socket
*/
public class Client implements Serializable {
private static Client instance;
private Socket clientSocket;
private BufferedReader in;
private PrintWriter out;
private ObjectOutputStream objectOutputStream;
private boolean connected = false;
private static final long serialVersionUID = 0L;
private Client() {}
public void connect(String targetAddress, String username_, int port_) {
try {
clientSocket = new Socket(targetAddress, port_);
clientSocket.setReuseAddress(true); // Allows same port to be used successively without cool-down
in = new BufferedReader(new InputStreamReader(clientSocket.getInputStream()));
out = new PrintWriter(clientSocket.getOutputStream(), true);
objectOutputStream = new ObjectOutputStream(clientSocket.getOutputStream());
objectOutputStream.writeObject(username_);
ClientController.getInstance().display("Attention: Successfully connected to server");
connected = true;
} catch (IOException e) {
ClientController.getInstance().display("Attention: Unable to connect to server");
}
}
public void disconnect() {
try {
if (in != null) {
in.close();
}
if (out != null) {
out.close();
}
if (objectOutputStream != null) {
objectOutputStream.close();
}
if (clientSocket != null) {
clientSocket.close();
}
} catch (IOException e) {
e.printStackTrace();
}
connected = false;
}
public boolean connected() {
return connected;
}
public static Client getInstance() {
if (instance == null) {
instance = new Client();
}
return instance;
}
public void sendMessage(String message) {
out.println(message);
}
}
客户端控制器:
package client;
import javafx.scene.layout.VBox;
/**
* Gabe Castelli
* 9/28/2016
* Description:
*/
public class ClientController implements Controller {
private static ClientController instance;
private ClientController() {}
public static ClientController getInstance() {
if (instance == null) {
instance = new ClientController();
}
return instance;
}
// Controls
public void display(String message) {
ClientGUI.getInstance().display(message);
}
public void displayAndSend(String message) {
display(message);
Client.getInstance().sendMessage(message);
}
public void connect(String address_, String username_, int port_) {
Client.getInstance().connect(address_, username_, port_);
}
public void disconnect() {
Client.getInstance().disconnect();
}
}
客户端图形用户界面:
package client;
import javafx.application.Application;
import javafx.fxml.FXMLLoader;
import javafx.geometry.Pos;
import javafx.scene.Parent;
import javafx.scene.Scene;
import javafx.scene.control.Button;
import javafx.scene.control.Label;
import javafx.scene.control.TextField;
import javafx.scene.input.KeyCode;
import javafx.scene.layout.VBox;
import javafx.stage.Stage;
/**
* Gabe Castelli
* 9/26/2016
* Description: GUI for client-side using JavaFX
*/
public class ClientGUI extends Application {
private Scene scene;
private TextField targetAddress;
private TextField fieldUsername;
private TextField fieldPort;
private VBox messages;
private TextField input;
private Button btnConnect;
private String username = "Anonymous";
private int port = 2000;
private static ClientGUI instance;
public static ClientGUI getInstance() {
if (instance == null) {
instance = new ClientGUI();
}
return instance;
}
@Override
public void start(Stage stage) throws Exception {
Parent root = FXMLLoader.load(getClass().getResource("ClientGUI.fxml"));
scene = new Scene(root, 480, 360);
stage.setTitle("MyMessenger");
stage.setScene(scene);
stage.setMinWidth(660);
stage.setMinHeight(495);
stage.setMaxWidth(990);
stage.show();
targetAddress = (TextField) scene.lookup("#targetAddress");
fieldUsername = (TextField) scene.lookup("#fieldUsername");
fieldPort = (TextField) scene.lookup("#fieldPort");
messages = (VBox) scene.lookup("#messages");
input = (TextField) scene.lookup("#input");
btnConnect = (Button) scene.lookup("#btnConnect");
input.setOnKeyPressed(event -> {
if (event.getCode() == KeyCode.ENTER) {
if (Client.getInstance().connected()) {
ClientController.getInstance().displayAndSend(input.getText());
input.setText("");
} else {
display("Attention: Not connected to server");
input.setText("");
}
}
});
btnConnect.setOnMouseClicked(event -> {
if (!Client.getInstance().connected()) {
if (!targetAddress.getText().equals("")) {
if (!fieldUsername.getText().equals("")) {
username = fieldUsername.getText();
}
if (!fieldPort.getText().equals("")) {
port = Integer.valueOf(fieldPort.getText());
}
ClientController.getInstance().connect(targetAddress.getText(), username, port);
btnConnect.setText("Disconnect");
} else {
display("Attention: Address required");
}
} else {
ClientController.getInstance().disconnect();
display("Attention: Disconnecting...");
btnConnect.setText("Connect");
}
});
}
public void display(String message) {
messages.getChildren().add(new Message(message));
}
}
堆栈跟踪:
Exception in thread "JavaFX Application Thread" java.lang.NullPointerException
at client.ClientGUI.display(ClientGUI.java:96)
at client.ClientController.display(ClientController.java:26)
at client.Client.connect(Client.java:34)
at client.ClientController.connect(ClientController.java:35)
at client.ClientGUI.lambda$start(ClientGUI.java:81)
at com.sun.javafx.event.CompositeEventHandler.dispatchBubblingEvent(CompositeEventHandler.java:86)
at com.sun.javafx.event.EventHandlerManager.dispatchBubblingEvent(EventHandlerManager.java:238)
at com.sun.javafx.event.EventHandlerManager.dispatchBubblingEvent(EventHandlerManager.java:191)
at com.sun.javafx.event.CompositeEventDispatcher.dispatchBubblingEvent(CompositeEventDispatcher.java:59)
at com.sun.javafx.event.BasicEventDispatcher.dispatchEvent(BasicEventDispatcher.java:58)
at com.sun.javafx.event.EventDispatchChainImpl.dispatchEvent(EventDispatchChainImpl.java:114)
at com.sun.javafx.event.BasicEventDispatcher.dispatchEvent(BasicEventDispatcher.java:56)
at com.sun.javafx.event.EventDispatchChainImpl.dispatchEvent(EventDispatchChainImpl.java:114)
at com.sun.javafx.event.EventUtil.fireEventImpl(EventUtil.java:74)
at com.sun.javafx.event.EventUtil.fireEvent(EventUtil.java:54)
at javafx.event.Event.fireEvent(Event.java:198)
at javafx.scene.Scene$ClickGenerator.postProcess(Scene.java:3470)
at javafx.scene.Scene$ClickGenerator.access00(Scene.java:3398)
at javafx.scene.Scene$MouseHandler.process(Scene.java:3766)
at javafx.scene.Scene$MouseHandler.access00(Scene.java:3485)
at javafx.scene.Scene.impl_processMouseEvent(Scene.java:1762)
at javafx.scene.Scene$ScenePeerListener.mouseEvent(Scene.java:2494)
at com.sun.javafx.tk.quantum.GlassViewEventHandler$MouseEventNotification.run(GlassViewEventHandler.java:352)
at com.sun.javafx.tk.quantum.GlassViewEventHandler$MouseEventNotification.run(GlassViewEventHandler.java:275)
at java.security.AccessController.doPrivileged(Native Method)
at com.sun.javafx.tk.quantum.GlassViewEventHandler.lambda$handleMouseEvent5(GlassViewEventHandler.java:388)
at com.sun.javafx.tk.quantum.QuantumToolkit.runWithoutRenderLock(QuantumToolkit.java:389)
at com.sun.javafx.tk.quantum.GlassViewEventHandler.handleMouseEvent(GlassViewEventHandler.java:387)
at com.sun.glass.ui.View.handleMouseEvent(View.java:555)
at com.sun.glass.ui.View.notifyMouse(View.java:937)
发生空指针异常是因为您从 ClientGUI.getInstance()
获得的 ClientGUI
实例不是作为 JavaFX 启动过程的一部分创建的实例,start()
方法被调用。由于 messages
在 start()
中初始化,您从 ClientGUI.getInstance()
获得的实例没有 messages
初始化。因此,当您调用 ClientGUI.getInstance().display(message)
时,它会调用 messages.getChildren()...
: messages
在 ClientGUI
实例中为空,您会得到一个空指针异常。
这里的根本问题是整体结构。你真的不能制作这些单例 - 特别是 Application
subclass - 因为它们是由框架实例化的。 ClientGUI
由 JavaFX 启动过程实例化,并且按照您使用 FXMLLoader
的方式,ClientController
由 FXMLLoader.load()
实例化(因此 ClientController.getInstance()
可能不是给你一个你希望它是的实例。
关于您为什么要尝试使这些 classes 成为单例,目前还不是很清楚,当然也没有任何意义。我的猜测是您正在尝试解决您现有的结构,这完全是非标准的。您定义的方法几乎都在 class 中,不应该对这些方法实现的功能负责。
特别是:Application
subclass 负责应用程序生命周期。它应该有一个 start
方法,可以简单地加载初始视图(FMXL 文件),将其放入 window 中并显示它。如果您 have/need 它们也可能会启动其他服务。如果需要,它可以选择覆盖 init()
和 stop()
。
控制器class负责更新视图。这是唯一应该引用 FXML 文件中定义的 UI 控件的地方,也是唯一应该修改显示的地方。
模型 class (Client
) 应该对视图或控制器一无所知。它应该只代表所呈现的状态或数据。根据模型表示的数据更新视图是控制器(实际上是一个演示者,在某些其他体系结构中这可以由视图完成)的责任。
因此,您可能希望按照以下几行重组应用程序。根据您可能需要的其他东西,这会有一些变化,但这至少应该让您走上可行的轨道:
客户端GUI:
public class ClientGUI extends Application {
@Override
public void start(Stage stage) throws Exception {
Parent root = FXMLLoader.load(getClass().getResource("ClientGUI.fxml"));
scene = new Scene(root, 480, 360);
stage.setTitle("MyMessenger");
stage.setScene(scene);
stage.setMinWidth(660);
stage.setMinHeight(495);
stage.setMaxWidth(990);
stage.show();
}
}
客户端控制器:
public class ClientController implements Controller {
@FXML
private TextField targetAddress;
@FXML
private TextField fieldUsername;
@FXML
private TextField fieldPort;
@FXML
private VBox messages;
@FXML
private TextField input;
@FXML
private Button btnConnect;
private String username = "Anonymous";
private int port = 2000;
private Client client ;
public void initialize() {
client = new Client();
}
@FXML
private void connect() {
if (!client.connected()) {
if (!targetAddress.getText().equals("")) {
if (!fieldUsername.getText().equals("")) {
username = fieldUsername.getText();
}
if (!fieldPort.getText().equals("")) {
port = Integer.valueOf(fieldPort.getText());
}
try {
client.connect(targetAddress.getText(), username, port);
btnConnect.setText("Disconnect");
display("Attention: Successfully connected to server");
} catch (IOException exc) {
display("Attention: Unable to connect to server");
}
} else {
display("Attention: Address required");
}
} else {
client.disconnect();
display("Attention: Disconnecting...");
btnConnect.setText("Connect");
}
}
@FXML
private void message() {
if (client.connected()) {
displayAndSend(input.getText());
input.setText("");
} else {
display("Attention: Not connected to server");
input.setText("");
}
}
public void display(String message) {
messages.getChildren().add(new Message(message));
}
public void displayAndSend(String message) {
display(message);
client.sendMessage(message);
}
}
然后是
// Note: why on earth is this Serializable? What state are you intending to serialize???
public class Client implements Serializable {
private Socket clientSocket;
private BufferedReader in;
private PrintWriter out;
private ObjectOutputStream objectOutputStream;
private boolean connected = false;
private static final long serialVersionUID = 0L;
public void connect(String targetAddress, String username_, int port_) throws IOException {
clientSocket = new Socket(targetAddress, port_);
clientSocket.setReuseAddress(true); // Allows same port to be used successively without cool-down
in = new BufferedReader(new InputStreamReader(clientSocket.getInputStream()));
out = new PrintWriter(clientSocket.getOutputStream(), true);
objectOutputStream = new ObjectOutputStream(clientSocket.getOutputStream());
objectOutputStream.writeObject(username_);
connected = true;
}
public void disconnect() {
try {
if (in != null) {
in.close();
}
if (out != null) {
out.close();
}
if (objectOutputStream != null) {
objectOutputStream.close();
}
if (clientSocket != null) {
clientSocket.close();
}
} catch (IOException e) {
e.printStackTrace();
}
connected = false;
}
public boolean connected() {
return connected;
}
public void sendMessage(String message) {
out.println(message);
}
}
要使控制器工作,您需要根元素定义一个 fx:controller="client.ClientController"
属性,并且 ClientGUI.fxml
中的控件定义与控制器中的字段名称匹配的 fx:id
属性class,并为要指定的事件处理程序方法,例如:
<!-- ... -->
<TextField fx:id="targetAddress" />
<TextField fx:id="fieldUserName" />
<TextField fx:id="fieldPort" />
<VBox fx:id="messages" />
<TextField fx:id="input" onAction="#message" />
<Button text="Connect" fx:id="btnConnect" onAction="#connect" />
<!-- ... -->
(请注意,当按下 enter 时,文本字段会触发 ActionEvent
,因此无需弄乱按键处理程序等)
您可能需要稍微修改一下以使应用程序的其余功能正常工作,但只要您坚持基本原则:只有控制器应该修改 UI; Client
class 应该不了解演示文稿的详细信息等,那么您就走对了。如果你做 anything static
,当然如果你试图做任何这些单例,你就做错了。