javafx - FXML 设置 ButtonType onAction

标签 javafx javafx-8 fxml

我正在 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/

相关文章:

javafx - 自动编号表行(javafx)

java - 如何在 javafx WebView 中指定默认字体与默认 UI 字体相同

java - 在 JavaFX 应用程序中使用 POJO 作为模型层

JavaFX 滚动 Pane 从不滚动

java - 您可以将变量传递给 FXML 中的 Insets 吗?

css - JavaFX 2.2 - 如何在 scrollPane 内缩放后调整滚动?

java - JavaFX 中的繁重渲染任务(在 Canvas 中)阻塞 GUI

java - 如何使 TreeView 中的 TreeItem 确认鼠标单击事件?

javafx : fxml: display elements twice

java - 安装了无限强度 jar 的非法 key 大小或默认参数