我正在 FXML 中构建一个 DialogPane,并且试图弄清楚如何响应对话框上的按钮按下,因为 onAction 不是 ButtonType 的有效参数。我已附上我的 FXML 和 Controller 类。关于 DialogPane 的文档很少,关于在 FXML 中执行此操作的文档更少,因此我不确定如何继续。
<?xml version="1.0" encoding="UTF-8"?>
<?import javafx.scene.control.ButtonType?>
<?import javafx.scene.control.DialogPane?>
<?import javafx.scene.control.Label?>
<?import javafx.scene.control.TextField?>
<?import javafx.scene.layout.ColumnConstraints?>
<?import javafx.scene.layout.GridPane?>
<?import javafx.scene.layout.RowConstraints?>
<DialogPane fx:id="loginPane" maxHeight="-Infinity" maxWidth="-Infinity" minHeight="-Infinity" minWidth="-Infinity" xmlns="http://javafx.com/javafx/8.0.60" xmlns:fx="http://javafx.com/fxml/1" fx:controller="LoginController">
<content>
<GridPane hgap="5.0" vgap="5.0">
<columnConstraints>
<ColumnConstraints hgrow="SOMETIMES" minWidth="10.0" prefWidth="100.0" />
<ColumnConstraints hgrow="SOMETIMES" minWidth="100.0" prefWidth="300.0" />
</columnConstraints>
<rowConstraints>
<RowConstraints minHeight="10.0" prefHeight="30.0" vgrow="SOMETIMES" />
<RowConstraints minHeight="10.0" prefHeight="30.0" vgrow="SOMETIMES" />
<RowConstraints minHeight="10.0" prefHeight="30.0" vgrow="SOMETIMES" />
<RowConstraints minHeight="10.0" prefHeight="30.0" vgrow="SOMETIMES" />
</rowConstraints>
<children>
<Label text="Driver Name" />
<TextField fx:id="driverTxt" GridPane.columnIndex="1" />
<Label text="URL" GridPane.rowIndex="1" />
<TextField fx:id="urlTxt" GridPane.columnIndex="1" GridPane.rowIndex="1" />
<Label text="Username" GridPane.rowIndex="2" />
<TextField fx:id="userTxt" GridPane.columnIndex="1" GridPane.rowIndex="2" />
<Label text="Password" GridPane.rowIndex="3" />
<TextField fx:id="passTxt" GridPane.columnIndex="1" GridPane.rowIndex="3" />
</children>
</GridPane>
</content>
<buttonTypes>
<ButtonType fx:id="loginButton" text="Login" />
<ButtonType fx:id="cancelBtn" text="Cancel" />
</buttonTypes>
</DialogPane>
import javafx.fxml.FXML;
import javafx.scene.Scene;
import javafx.scene.control.ButtonType;
import javafx.scene.control.DialogPane;
import javafx.scene.control.TextField;
public class LoginController {
@FXML
DialogPane loginPane;
@FXML
TextField driverTxt;
@FXML
TextField urlTxt;
@FXML
TextField userTxt;
@FXML
TextField passTxt;
@FXML
ButtonType loginButton;
@FXML
private void loginButtonAction(){
// How do I do something here
}
public void initialize() {
driverTxt.setText("org.postgresql.Driver");
urlTxt.setText("jdbc:postgresql://localhost/postgres");
userTxt.setText("postgres");
passTxt.setText("postgres");
}
}
最佳答案
通常您不需要这样做。您通常会显示 DialogPane
在 Dialog<ButtonType>
调用其 showAndWait()
方法,返回 Optional<ButtonType>
代表按下的按钮(如果有)。所以正常使用会是这样的
public class LoginController {
public static final ButtonType LOGIN = new ButtonType("Login");
@FXML
DialogPane loginPane;
@FXML
TextField driverTxt;
@FXML
TextField urlTxt;
@FXML
TextField userTxt;
@FXML
TextField passTxt;
public void initialize() {
driverTxt.setText("org.postgresql.Driver");
urlTxt.setText("jdbc:postgresql://localhost/postgres");
userTxt.setText("postgres");
passTxt.setText("postgres");
}
public String getDriver() {
return driverTxt.getText();
}
public String getUrl() {
return urlTxt.getText();
}
public String getUser() {
return userTxt.getText();
}
public String getPass() {
return pass.getText();
}
}
并对您的 FXML 文件进行以下更改:
<buttonTypes>
<LoginController fx:constant="LOGIN" />
<ButtonType fx:constant="CANCEL" />
</buttonTypes>
然后你可以使用它:
Dialog<ButtonType> dialog = new Dialog<>();
FXMLLoader dialogLoader = new FXMLLoader(getClass().getResource("Login.fxml"));
dialog.setDialogPane(dialogLoader.load());
LoginController controller = dialogLoader.getController();
dialog.showAndWait().filter(LoginController.LOGIN::equals)
.ifPresent(button -> {
String driver = controller.getDriver();
// etc etc
// process login...
});
作为从文本字段公开文本的替代方法,您可以定义 processLogin()
Controller 本身中的方法读取文本字段并执行您需要执行的操作:
public class LoginController {
public static final ButtonType LOGIN = new ButtonType("Login");
@FXML
DialogPane loginPane;
@FXML
TextField driverTxt;
@FXML
TextField urlTxt;
@FXML
TextField userTxt;
@FXML
TextField passTxt;
public void initialize() {
driverTxt.setText("org.postgresql.Driver");
urlTxt.setText("jdbc:postgresql://localhost/postgres");
userTxt.setText("postgres");
passTxt.setText("postgres");
}
public void processLogin() {
String driver = driverTxt.getText();
// etc...
// process login...
}
}
然后就这样做
// ...
dialog.showAndWait().filter(LoginController.LOGIN::equals)
.ifPresent(button -> controller.processLogin());
如果您确实需要注册onAction
带有登录按钮的处理程序,在 initialize()
中执行此操作 Controller 中的方法:
public void initialize() {
driverTxt.setText("org.postgresql.Driver");
urlTxt.setText("jdbc:postgresql://localhost/postgres");
userTxt.setText("postgres");
passTxt.setText("postgres");
Button login = (Button) loginPane.lookupButton(loginButton);
login.setOnAction(e -> { /* ... */ });
}
但这确实违背了对话框 Pane API 的预期用途。
最后一个替代方案是覆盖 createButton
DialogPane
中的方法。为此,您需要 DialogPane
的子类,这意味着使用 FXML custom component pattern .
所以这看起来像:
public class LoginPane extends DialogPane {
public static final ButtonType LOGIN = new ButtonType("Login");
@FXML
TextField driverTxt;
@FXML
TextField urlTxt;
@FXML
TextField userTxt;
@FXML
TextField passTxt;
public LoginPane() {
try {
FXMLLoader loader = new FXMLLoader(getClass().getResource("LoginPane.fxml"));
loader.setRoot(this);
loader.setController(this);
loader.load();
} catch (IOException exc) {
// bad if you get here...
throw new UncheckedIOException(exc);
}
}
@Override
public Node createButton(ButtonType buttonType) {
Node button = super.createButton(buttonType);
if (buttonType == LOGIN) {
((Button) button).setOnAction(e -> processLogin());
}
return button ;
}
public void initialize() {
driverTxt.setText("org.postgresql.Driver");
urlTxt.setText("jdbc:postgresql://localhost/postgres");
userTxt.setText("postgres");
passTxt.setText("postgres");
}
public void processLogin() {
String driver = driverTxt.getText();
// etc...
// process login...
}
}
FXML 将如下所示
<?xml version="1.0" encoding="UTF-8"?>
<?import javafx.scene.control.ButtonType?>
<?import javafx.scene.control.DialogPane?>
<?import javafx.scene.control.Label?>
<?import javafx.scene.control.TextField?>
<?import javafx.scene.layout.ColumnConstraints?>
<?import javafx.scene.layout.GridPane?>
<?import javafx.scene.layout.RowConstraints?>
<fx:root type="DialogPane" maxHeight="-Infinity" maxWidth="-Infinity" minHeight="-Infinity" minWidth="-Infinity" xmlns="http://javafx.com/javafx/8.0.60" xmlns:fx="http://javafx.com/fxml/1">
<content>
<GridPane hgap="5.0" vgap="5.0">
<columnConstraints>
<ColumnConstraints hgrow="SOMETIMES" minWidth="10.0" prefWidth="100.0" />
<ColumnConstraints hgrow="SOMETIMES" minWidth="100.0" prefWidth="300.0" />
</columnConstraints>
<rowConstraints>
<RowConstraints minHeight="10.0" prefHeight="30.0" vgrow="SOMETIMES" />
<RowConstraints minHeight="10.0" prefHeight="30.0" vgrow="SOMETIMES" />
<RowConstraints minHeight="10.0" prefHeight="30.0" vgrow="SOMETIMES" />
<RowConstraints minHeight="10.0" prefHeight="30.0" vgrow="SOMETIMES" />
</rowConstraints>
<children>
<Label text="Driver Name" />
<TextField fx:id="driverTxt" GridPane.columnIndex="1" />
<Label text="URL" GridPane.rowIndex="1" />
<TextField fx:id="urlTxt" GridPane.columnIndex="1" GridPane.rowIndex="1" />
<Label text="Username" GridPane.rowIndex="2" />
<TextField fx:id="userTxt" GridPane.columnIndex="1" GridPane.rowIndex="2" />
<Label text="Password" GridPane.rowIndex="3" />
<TextField fx:id="passTxt" GridPane.columnIndex="1" GridPane.rowIndex="3" />
</children>
</GridPane>
</content>
<buttonTypes>
<LoginPane fx:constant="LOGIN" />
<ButtonType fx:constant="CANCEL" />
</buttonTypes>
</fx:root>
您可以将此版本用于
Dialog dialog = new Dialog();
dialog.setDialogPane(new LoginPane());
dialog.showAndWait();
因此,如果您希望将尽可能多的内容封装到登录 Pane 和 fxml 中,这可能是最简洁的选择。
请注意 DialogPane
的用法 Dialog
的 API 文档中有相当完整的记录和 DialogPane
.
关于javafx - FXML 设置 ButtonType onAction,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/39004086/