如何在 javafx 中关闭弹出窗口 windows?
How to close pop up windows in javafx?
我有两个场景 - 登录提示和主屏幕。 openLoginPrompt() 函数打开登录提示。然后我调用 handleLogin() 函数来尝试对用户进行身份验证。登录成功后,我想关闭登录提示并 return 到主屏幕。但是,如果我不将 primaryStage 设置为静态 primaryStage,则在调用 handleLogin() 函数时似乎为空。为什么会出现这种情况,是否有更好的关闭阶段的替代方法?
/*
* To change this license header, choose License Headers in Project Properties.
* To change this template file, choose Tools | Templates
* and open the template in the editor.
*/
package chatapplication;
import java.io.IOException;
import java.sql.Connection;
import java.sql.PreparedStatement;
import java.sql.ResultSet;
import java.sql.SQLException;
import javafx.fxml.FXML;
import javafx.fxml.FXMLLoader;
import javafx.scene.Scene;
import javafx.scene.control.Button;
import javafx.scene.control.Label;
import javafx.scene.control.PasswordField;
import javafx.scene.control.TextField;
import javafx.scene.layout.AnchorPane;
import javafx.stage.Stage;
/**
*
* @author babaji
*/
public class Controller {
private static Stage primaryStage;
@FXML
private Button openLoginPrompt;
@FXML
private Button login;
@FXML
private TextField username;
@FXML
private PasswordField password;
@FXML
private Label loginMessage;
@FXML
private AnchorPane loginPrompt;
@FXML
private void openLoginPrompt() {
try {
primaryStage = new Stage();
// Load root layout from fxml file.
FXMLLoader loader = new FXMLLoader();
loader.setLocation(ChatApplication.class.getResource("LoginPrompt.fxml"));
AnchorPane rootLayout = (AnchorPane) loader.load();
// Show the scene containing the root layout.
Scene scene = new Scene(rootLayout);
primaryStage.setScene(scene);
primaryStage.show();
} catch (IOException e) {
e.printStackTrace();
}
}
@FXML
private void handleLogin() {
Database dbObj = new Database();
Connection conn = dbObj.connectToDb("chat");
String uname = username.getText();
String psswd = password.getText();
ResultSet rs = null;
try {
String sql = "SELECT * FROM user WHERE user_name=? AND password=?";
PreparedStatement ps = conn.prepareStatement(sql);
ps.setString(1, uname);
ps.setString(2, psswd);
rs = ps.executeQuery();
}
catch(SQLException e) {
e.printStackTrace();
}
int count = 0;
try {
if(rs != null) while(rs.next()) count++;
}
catch(SQLException e) {
count = 0;
}
if(count == 1) {
System.out.println("Successfully Logged in");
/*This is where the problem Lies. If I dont't set primaryStage as
static, primaryStage returns null. Why does this happen and is there
some other way to close the window?s
*/
if(primaryStage != null) primaryStage.close();
}
else {
System.out.println("Failed to login.");
loginMessage.setText("Incorrect username or password");
}
}
}
您有两个控制器实例,一个用于 LoginPrompt.fxml
,一个用于您未显示的另一个 FXML(我们称它为 "main" fxml)。 openLoginPrompt
在 "main" 实例上调用,并初始化 primaryStage
,并加载 LoginPrompt.fxml
。当您加载 FXML 时,FXMLLoader
会创建一个新的控制器实例,它没有 primaryStage
初始化(假设它不是 static
);所以当在 LoginPrompt.fxml
控制器上调用 handleLogin()
时,它是 null
.
对两个不同的 FXML 文件使用同一个控制器 class 是一个非常糟糕的主意,因为它会导致各种令人困惑的场景,例如在 class。您应该为每个 FXML 文件使用不同的控制器 class。
在这种情况下,您可以定义一个 LoginController
class。编辑 LoginPrompt.fxml
以使用此控制器 class。您可以从 class 公开各种属性,并在打开登录 window:
时从主控制器观察它们
public class LoginController {
@FXML
private Label loginMessage ;
private final BooleanProperty loggedIn = new SimpleBooleanProperty();
public BooleanProperty loggedInProperty() {
return loggedIn ;
}
public final boolean isLoggedIn() {
return loggedInProperty().get();
}
public final void setLoggedIn(boolean loggedIn) {
loggedInProperty().set(loggedIn);
}
@FXML
private void handleLogin() {
// existing code you had in the previous version...
if (count == 1) {
System.out.println("Successfully logged in");
setLoggedIn(true);
} else {
System.out.println("Failed to login.");
loginMessage.setText("Incorrect username or password");
}
}
}
然后在你的主控制器中你做
@FXML
private void openLoginPrompt() {
try {
Stage loginStage = new Stage();
// Load root layout from fxml file.
FXMLLoader loader = new FXMLLoader();
loader.setLocation(ChatApplication.class.getResource("LoginPrompt.fxml"));
AnchorPane rootLayout = (AnchorPane) loader.load();
LoginController loginController = loader.getController();
loginController.loggedInProperty().addListener((obs, wasLoggedIn, isNowLoggedIn) -> {
if (isNowLoggedIn) {
loginStage.hide();
}
});
// Show the scene containing the root layout.
Scene scene = new Scene(rootLayout);
loginStage.setScene(scene);
loginStage.show();
} catch (IOException e) {
e.printStackTrace();
}
}
此版本无需将 Stage
设为字段。
我还应该提到有一种 "quick and dirty" 方法可以直接从 LoginController
:
关闭舞台
loginMessage.getScene().getWindow().hide();
这将避免需要 属性 甚至从主控制器获取对 LoginController
的引用。然而,您经常遇到这样的情况,您可能需要来自另一个控制器的数据,而第一种技术是一种更普遍有用的方法。
我有两个场景 - 登录提示和主屏幕。 openLoginPrompt() 函数打开登录提示。然后我调用 handleLogin() 函数来尝试对用户进行身份验证。登录成功后,我想关闭登录提示并 return 到主屏幕。但是,如果我不将 primaryStage 设置为静态 primaryStage,则在调用 handleLogin() 函数时似乎为空。为什么会出现这种情况,是否有更好的关闭阶段的替代方法?
/*
* To change this license header, choose License Headers in Project Properties.
* To change this template file, choose Tools | Templates
* and open the template in the editor.
*/
package chatapplication;
import java.io.IOException;
import java.sql.Connection;
import java.sql.PreparedStatement;
import java.sql.ResultSet;
import java.sql.SQLException;
import javafx.fxml.FXML;
import javafx.fxml.FXMLLoader;
import javafx.scene.Scene;
import javafx.scene.control.Button;
import javafx.scene.control.Label;
import javafx.scene.control.PasswordField;
import javafx.scene.control.TextField;
import javafx.scene.layout.AnchorPane;
import javafx.stage.Stage;
/**
*
* @author babaji
*/
public class Controller {
private static Stage primaryStage;
@FXML
private Button openLoginPrompt;
@FXML
private Button login;
@FXML
private TextField username;
@FXML
private PasswordField password;
@FXML
private Label loginMessage;
@FXML
private AnchorPane loginPrompt;
@FXML
private void openLoginPrompt() {
try {
primaryStage = new Stage();
// Load root layout from fxml file.
FXMLLoader loader = new FXMLLoader();
loader.setLocation(ChatApplication.class.getResource("LoginPrompt.fxml"));
AnchorPane rootLayout = (AnchorPane) loader.load();
// Show the scene containing the root layout.
Scene scene = new Scene(rootLayout);
primaryStage.setScene(scene);
primaryStage.show();
} catch (IOException e) {
e.printStackTrace();
}
}
@FXML
private void handleLogin() {
Database dbObj = new Database();
Connection conn = dbObj.connectToDb("chat");
String uname = username.getText();
String psswd = password.getText();
ResultSet rs = null;
try {
String sql = "SELECT * FROM user WHERE user_name=? AND password=?";
PreparedStatement ps = conn.prepareStatement(sql);
ps.setString(1, uname);
ps.setString(2, psswd);
rs = ps.executeQuery();
}
catch(SQLException e) {
e.printStackTrace();
}
int count = 0;
try {
if(rs != null) while(rs.next()) count++;
}
catch(SQLException e) {
count = 0;
}
if(count == 1) {
System.out.println("Successfully Logged in");
/*This is where the problem Lies. If I dont't set primaryStage as
static, primaryStage returns null. Why does this happen and is there
some other way to close the window?s
*/
if(primaryStage != null) primaryStage.close();
}
else {
System.out.println("Failed to login.");
loginMessage.setText("Incorrect username or password");
}
}
}
您有两个控制器实例,一个用于 LoginPrompt.fxml
,一个用于您未显示的另一个 FXML(我们称它为 "main" fxml)。 openLoginPrompt
在 "main" 实例上调用,并初始化 primaryStage
,并加载 LoginPrompt.fxml
。当您加载 FXML 时,FXMLLoader
会创建一个新的控制器实例,它没有 primaryStage
初始化(假设它不是 static
);所以当在 LoginPrompt.fxml
控制器上调用 handleLogin()
时,它是 null
.
对两个不同的 FXML 文件使用同一个控制器 class 是一个非常糟糕的主意,因为它会导致各种令人困惑的场景,例如在 class。您应该为每个 FXML 文件使用不同的控制器 class。
在这种情况下,您可以定义一个 LoginController
class。编辑 LoginPrompt.fxml
以使用此控制器 class。您可以从 class 公开各种属性,并在打开登录 window:
public class LoginController {
@FXML
private Label loginMessage ;
private final BooleanProperty loggedIn = new SimpleBooleanProperty();
public BooleanProperty loggedInProperty() {
return loggedIn ;
}
public final boolean isLoggedIn() {
return loggedInProperty().get();
}
public final void setLoggedIn(boolean loggedIn) {
loggedInProperty().set(loggedIn);
}
@FXML
private void handleLogin() {
// existing code you had in the previous version...
if (count == 1) {
System.out.println("Successfully logged in");
setLoggedIn(true);
} else {
System.out.println("Failed to login.");
loginMessage.setText("Incorrect username or password");
}
}
}
然后在你的主控制器中你做
@FXML
private void openLoginPrompt() {
try {
Stage loginStage = new Stage();
// Load root layout from fxml file.
FXMLLoader loader = new FXMLLoader();
loader.setLocation(ChatApplication.class.getResource("LoginPrompt.fxml"));
AnchorPane rootLayout = (AnchorPane) loader.load();
LoginController loginController = loader.getController();
loginController.loggedInProperty().addListener((obs, wasLoggedIn, isNowLoggedIn) -> {
if (isNowLoggedIn) {
loginStage.hide();
}
});
// Show the scene containing the root layout.
Scene scene = new Scene(rootLayout);
loginStage.setScene(scene);
loginStage.show();
} catch (IOException e) {
e.printStackTrace();
}
}
此版本无需将 Stage
设为字段。
我还应该提到有一种 "quick and dirty" 方法可以直接从 LoginController
:
loginMessage.getScene().getWindow().hide();
这将避免需要 属性 甚至从主控制器获取对 LoginController
的引用。然而,您经常遇到这样的情况,您可能需要来自另一个控制器的数据,而第一种技术是一种更普遍有用的方法。