首页 文章

FXML设置ButtonType onAction

提问于
浏览
0

我在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");
    }

}

1 回答

  • 3

    通常,您不需要这样做 . 你通常会在Dialog<ButtonType>中调用 showAndWait() 来调用它的 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() 方法,该方法读取文本字段并执行您需要执行的操作:

    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() 方法中执行此操作:

    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 -> { /* ... */ });
    }
    

    但这实际上违背了对话框窗格API的预期用途 .


    最后一种选择是覆盖 DialogPane 中的 createButton 方法 . 要做到这一点,你需要一个 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();
    

    因此,如果您希望尽可能地将其封装到登录窗格和fxml中,这可能是最干净的选择 .

    请注意 DialogPane 的使用完全记录在DialogDialogPane的API文档中 .

相关问题