JavaFx - 获取其父节点不是 Pane 的节点或控件的坐标 (x,y)

JavaFx - Get the coordinates(x,y) of a node or control whose parent is not a Pane

我正在尝试模拟一个弹出容器,它会显示一些关于正确地址格式的提示。只要 addressTextfield 获得焦点,就会显示弹出窗口。弹出窗口应出现在 addressTextfield 下方。问题是,我在布局中使用了 VBoxHBoxFlowPane 的组合。默认的 layoutXlayoutY 是零,看起来它们在运行时不会改变。

我的弹出控件

我的地址字段和层次结构

到目前为止我得到了什么...

我的 Popup 控件保持在顶部。我尝试将我的弹出窗口 layoutXProperty() 绑定到 addressTextFieldslayoutXProperty()same in layoutYProperty()。我尝试在 addressTextField 上添加一个 MouseEvent.MOUSE_CLICKED,添加一个将在 MouseEvent.MouseClicked 上更新的 MouseLocationProperty,并将其绑定到我的弹出窗口的 layoutXProperty()。但是仍然没有让它出现在 addressTextField.

我的添加新帐户Class

package com.strongandwhite.pages.dentistadnstaff;

import com.strongandwhite.customcontrols.CustomComboBox;
import com.strongandwhite.customcontrols.CustomDatePicker;
import com.strongandwhite.customcontrols.CustomPasswordField;
import com.strongandwhite.customcontrols.CustomTextField;
import com.strongandwhite.main.MainWindow;
import com.strongandwhite.models.User;
import com.strongandwhite.pages.Page;
import com.strongandwhite.rules.*;
import javafx.beans.property.SimpleDoubleProperty;
import javafx.fxml.FXML;
import javafx.fxml.FXMLLoader;
import javafx.scene.control.Button;
import javafx.scene.control.DateCell;
import javafx.scene.control.ScrollPane;
import javafx.scene.layout.AnchorPane;
import javafx.scene.layout.HBox;
import javafx.scene.layout.Pane;

import java.io.IOException;
import java.time.LocalDate;
import java.time.chrono.ChronoLocalDate;
import java.util.ArrayList;

public class AddNewAccount extends Page {
    private ArrayList<String> gender;
    private ArrayList<String> type;
    private ArrayList<String> position;
    public User user;
    private SimpleDoubleProperty mouseX;
    private SimpleDoubleProperty mouseY;

    @FXML private HBox firstNameHBox;
    private CustomTextField firstNameTextField;

    @FXML private HBox middleNameHBox;
    private CustomTextField middleNameTextField;

    @FXML private HBox lastNameHBox;
    private CustomTextField lastNameTextField;

    @FXML private HBox ageHBox;
    private CustomTextField ageTextField;

    @FXML private HBox genderHBox;
    private CustomComboBox<String> genderComboBox;

    @FXML private HBox addressHBox;
    private CustomTextField addressTextField;

    @FXML private HBox contactHBox;
    private CustomTextField contactTextField;

    @FXML private HBox nationalityHBox;
    private CustomTextField nationalityTextField;

    @FXML private HBox religionHBox;
    private CustomTextField religionTextField;

    @FXML private HBox emailAddressHBox;
    private CustomTextField emailAddressTextField;

    @FXML private HBox birthdateHBox;
    private CustomDatePicker birtdateDatePicker;

    @FXML private HBox accountIDHBox;
    private CustomTextField accountIDTextField;

    @FXML private HBox passwordHBox;
    private CustomPasswordField passwordTextField;

    @FXML private HBox confirmPasswordHBox;
    private CustomPasswordField confirmPasswordTextField;

    @FXML private HBox accountTypeHBox;
    private CustomComboBox<String> accountTypeComboBox;

    @FXML private HBox positionHBox;
    private CustomComboBox<String> positionComboBox;

    @FXML private HBox question1HBox;
    private CustomTextField question1TextField;

    @FXML private HBox question2HBox;
    private CustomTextField question2TextField;

    @FXML private HBox question3HBox;
    private CustomTextField question3TextField;

    @FXML private HBox answer1HBox;
    private CustomTextField answer1TextField;

    @FXML private HBox answer2HBox;
    private CustomTextField answer2TextField;

    @FXML private HBox answer3HBox;
    private CustomTextField answer3TextField;

    @FXML private Button clearButton;

    private AnchorPane addressInfo;
    private Pane coverPane;
    private ScrollPane scrollPane;

    public AddNewAccount(User user, MainWindow mainWindow) {
        this.user = user;

        FXMLLoader loader = new FXMLLoader(getClass().getResource("AddNewAccount.fxml"));
        loader.setController(this);
        loader.setRoot(this);
        try {
            loader.load();
        } catch (IOException e) {
            e.printStackTrace();
        }
        loadTitle("AddNewAccountTitle.fxml");

        FXMLLoader loader1 = new FXMLLoader(getClass().getResource("AddressInfo.fxml"));
        try {
            addressInfo = loader1.load();
        } catch (IOException e) {
            e.printStackTrace();
        }

        coverPane = new Pane();

        firstNameTextField = new CustomTextField(firstNameHBox, customControls);
        firstNameTextField.setRule(new NameRule("First Name"));

        middleNameTextField = new CustomTextField(middleNameHBox, customControls);
        middleNameTextField.setRule(new NameRule("Middle Name"));

        lastNameTextField = new CustomTextField(lastNameHBox, customControls);
        lastNameTextField.setRule(new NameRule("Last Name"));

        birtdateDatePicker = new CustomDatePicker(birthdateHBox, customControls);
        birtdateDatePicker.setRule(new DateRule());

        ageTextField = new CustomTextField(ageHBox, customControls);
        ageTextField.setRule(new NumbersOnlyRule());

        genderComboBox = new CustomComboBox<>(genderHBox, customControls);
        genderComboBox.setRule(new WordRule("Gender"));

        addressTextField = new CustomTextField(addressHBox, customControls);
        addressTextField.setRule(new AddressRule());

        contactTextField = new CustomTextField(contactHBox, customControls);
        contactTextField.setRule(new ContactNumberRule());

        nationalityTextField = new CustomTextField(nationalityHBox, customControls);
        nationalityTextField.setRule(new NameRule("Nationality"));

        religionTextField = new CustomTextField(religionHBox, customControls);
        religionTextField.setRule(new NameRule("Religion"));

        emailAddressTextField = new CustomTextField(emailAddressHBox, customControls);
        emailAddressTextField.setRule(new EmailAddressRule());

        accountIDTextField = new CustomTextField(accountIDHBox, customControls);
        accountIDTextField.setRule(new AccountIDRule());

        passwordTextField = new CustomPasswordField(passwordHBox, customControls);
        passwordTextField.setRule(new PasswordRule());

        confirmPasswordTextField = new CustomPasswordField(confirmPasswordHBox, customControls);
        confirmPasswordTextField.setRule(new PasswordRule());

        accountTypeComboBox = new CustomComboBox<>(accountTypeHBox, customControls);
        accountTypeComboBox.setRule(new WordRule("Account Type"));

        positionComboBox = new CustomComboBox<>(positionHBox, customControls);
        positionComboBox.setRule(new WordRule("Position"));

        question1TextField = new CustomTextField(question1HBox, customControls);
        question1TextField.setRule(new WordRule("Question"));

        question2TextField = new CustomTextField(question2HBox, customControls);
        question2TextField.setRule(new WordRule("Question"));

        question3TextField = new CustomTextField(question3HBox, customControls);
        question3TextField.setRule(new WordRule("Question"));

        answer1TextField = new CustomTextField(answer1HBox, customControls);
        answer1TextField.setRule(new WordRule("Answer"));

        answer2TextField = new CustomTextField(answer2HBox, customControls);
        answer2TextField.setRule(new WordRule("Answer"));

        answer3TextField = new CustomTextField(answer3HBox, customControls);
        answer3TextField.setRule(new WordRule("Answer"));

        clearButton.setOnAction(event -> clearPageFields());

        birtdateDatePicker.setDefaultDate(LocalDate.now().minusYears(18));
        birtdateDatePicker.setDayCellFactory(param -> new DateCell() {
            @Override
            public void updateItem(LocalDate item, boolean empty) {
                super.updateItem(item, empty);
                if(item.isBefore(ChronoLocalDate.from(LocalDate.now().minusYears(76).minusMonths(1)))||item.isAfter(ChronoLocalDate.from(LocalDate.now().minusYears(18).plusMonths(1)))) {
                    this.setDisable(true);
                }
            }
        });

        user.ageProperty().bind(birtdateDatePicker.ageProperty());
        ageTextField.textProperty().bind(user.ageProperty().asString());

        gender = new ArrayList<>();
        gender.add("Male");
        gender.add("Female");
        genderComboBox.setItems(gender);

        type = new ArrayList<>();
        type.add("Administrator");
        type.add("Standard");
        type.add("Limited");
        accountTypeComboBox.setItems(type);

        position = new ArrayList<>();
        position.add("Dentist");
        position.add("Receptionist");
        position.add("Staff");
        positionComboBox.setItems(position);

        answer1TextField.editableProperty().bind(question1TextField.textProperty().isNotEmpty());
        answer1TextField.editableProperty().addListener(event-> {
            if(question1TextField.textProperty().getValue().trim().isEmpty()) answer1TextField.clear();
        });

        answer2TextField.editableProperty().bind(question2TextField.textProperty().isNotEmpty());
        answer2TextField.editableProperty().addListener(event-> {
            if(question2TextField.textProperty().getValue().trim().isEmpty()) answer2TextField.clear();
        });

        answer3TextField.editableProperty().bind(question3TextField.textProperty().isNotEmpty());
        answer3TextField.editableProperty().addListener(event-> {
            if(question3TextField.textProperty().getValue().trim().isEmpty()) answer3TextField.clear();
        });

        mainWindow.getActivePagePane().getChildren().add(getPageTitle());

        scrollPane = (ScrollPane) mainWindow.getActivePagePane().getChildren().get(0);
        scrollPane.vvalueProperty().addListener(event-> scrollPane.requestFocus());

        addressInfo.visibleProperty().bind(addressTextField.getTextField().focusedProperty());
        coverPane.setPickOnBounds(false);
        mainWindow.getActivePagePane().getChildren().add(coverPane);
        coverPane.getChildren().add(addressInfo);
        addressInfo.layoutXProperty().bind(addressTextField.getTextField().layoutXProperty());
        addressInfo.layoutYProperty().bind(addressTextField.getTextField().layoutYProperty());
    }
}

我的自定义文本字段Class

package com.strongandwhite.customcontrols;

import javafx.beans.property.BooleanProperty;
import javafx.beans.property.StringProperty;
import javafx.scene.control.Button;
import javafx.scene.control.Label;
import javafx.scene.control.TextField;
import javafx.scene.layout.HBox;
import javafx.scene.layout.StackPane;
import javafx.scene.layout.VBox;

import java.util.ArrayList;

public class CustomTextField extends CustomControl {
    private TextField textField;
    private Label textLabel;
    private Button clearButton;

    public CustomTextField(HBox control, ArrayList<CustomControl> customControls) {
        setContainer(control);
        textLabel = (Label) ((VBox)control.getChildren().get(0)).getChildren().get(0);
        setErrorLabel((Label) ((VBox)control.getChildren().get(0)).getChildren().get(2));
        textField = (TextField) ((StackPane)((VBox)control.getChildren().get(0)).getChildren().get(1)).getChildren().get(0);
        clearButton = (Button) ((StackPane)((VBox)control.getChildren().get(0)).getChildren().get(1)).getChildren().get(1);
        textLabel.visibleProperty().bind(textField.textProperty().isNotEmpty());
        clearButton.visibleProperty().bind(textField.textProperty().isNotEmpty().and(textField.focusedProperty()).and(textField.editableProperty()));
        clearButton.setOnAction(event -> textField.clear());
        textField.textProperty().addListener((observable, oldValue, newValue) -> executeRule(getRule(), textField.getText()));
        customControls.add(this);
    }

    public StringProperty textProperty() {
        return textField.textProperty();
    }

    public BooleanProperty editableProperty() {
        return textField.editableProperty();
    }

    public String getText() {
        return textField.getText();
    }

    public void setText(String text) {
        textField.setText(text);
    }

    public TextField getTextField() {
        return textField;
    }

    public void setTextField(TextField textField) {
        this.textField = textField;
    }

    @Override
    public void clear() {
        textField.clear();
    }
}

有什么方法可以获取 addressTextField 相对于我的表单的坐标吗?

注意:我的弹出窗口添加在 Pane 特别是 coverPane 上面,它们都在 StackPane.

建议您使用类似的东西:

Bounds boundsInScene = addressTextField.localToScene(addressTextField.getBoundsInLocal());

然后使用

boundsInScene.getMinX()
boundsInScene.getMinY()
boundsInScene.getMaxX()
boundsInScene.getMaxY()
boundsInScene.getWidth()
boundsInScene.getHeight() 

获取组件相对于场景的值