JavaFx как получить Stage из Controller?

323
06 октября 2021, 07:50

Структура проект такой

В App получаю контроллер MainXMLController и через метод setStage() устанавливаю primaryStage.

App.java

public void start(Stage primaryStage){
    try{
        FXMLLoader fxmlLoader = new FXMLLoader(getClass().getResource("/views/main.xml.fxml")); // FXMLLoader.load(getClass().getResource("/views/main.xml.fxml"));
        Parent root = fxmlLoader.load();
        MainXMLController mainXMLController = fxmlLoader.getController();
        Scene scene = new Scene(root);
        primaryStage.setScene(scene);
        primaryStage.initStyle(StageStyle.UNDECORATED);
        mainXMLController.setPrimaryStage(primaryStage);
        primaryStage.show();
    } catch (Exception e) {
        e.printStackTrace();
    }
}

MainXMLController.java

public class MainXMLController extends  BaseController implements Initializable{
    private Stage primaryStage;
    private static double xOffset = 0;
    private static double yOffset = 0;
    @FXML
    private FlowPane titleBar;
    @FXML
    private FlowPane firstKeyboardRow, secondKeyboardRow, thirdKeyboardRow;
    @FXML
    private TextArea typeIt;
    @Override
    public void initialize(URL url, ResourceBundle resourceBundle) {
        dragUndecoratedWindow();
        setKeyboardThemes();
        setTypeItText();
        installEventHandlerStage();
    }
    public void setTypeItText(){
        typeIt.setText(new DefaultTextModel().getTypeText());
    }
    public void setPrimaryStage(Stage stage){
        primaryStage = stage;
    }
    /* ActionListners  */
    private void println(Object object){
        System.out.println(object);
    }
    private void dragUndecoratedWindow(){
           titleBar.setOnMousePressed(new EventHandler<MouseEvent>() {
            @Override
            public void handle(MouseEvent mouseEvent) {
                xOffset = primaryStage.getX() - mouseEvent.getScreenX();
                yOffset = primaryStage.getY() - mouseEvent.getScreenY();
                println("stageFROM event " + primaryStage.getY() );
            }
        });
        titleBar.setOnMouseDragged(new EventHandler<MouseEvent>() {
            @Override
            public void handle(MouseEvent mouseEvent) {
                primaryStage.setX(mouseEvent.getScreenX() + xOffset);
                primaryStage.setY(mouseEvent.getScreenY() + yOffset);
            }
        });
    }
    private void installEventHandlerStage(){
       EventHandler<KeyEvent> keyEventEventHandler = new EventHandler<KeyEvent>() {
           @Override
           public void handle(KeyEvent keyEvent) {
               System.out.println(keyEvent.getCode());
           }
       };
        //println("stageFROM event " + primaryStage.getY() );
       primaryStage.addEventFilter(KeyEvent.KEY_TYPED, keyEventEventHandler);
    }
}

Главный метод перемещения работает а вот когда хочу установить EventHandler получаю NullPointer? Почему так работает? Или когда программа запускается стейдж не доступен?

Answer 1

Метод public void initialize вызывается во время прогрузки fxml (т.е. во время выполнения fxmlLoader.load()), и в этот момент primaryStage ещё не прокинут контроллеру. Варианты решения:

  • переместить вызов installEventHandlerStage в setPrimaryStage;
  • вызвать installEventHandlerStage самостоятельно после setPrimaryStage;
  • создать конструктор класса public MainXMLController( Stage stage ){...}, убрать упоминания этого класса в fxml и самостоятельно установить контроллер

    FXMLLoader fxmlLoader = new FXMLLoader(getClass().getResource("/views/main.xml.fxml"));
    MainXMLController mainXMLController = new MainXMLController( primaryStage );
    fxmlLoader.setController( mainXMLController );
    Parent root = fxmlLoader.load();
    
  • тут ещё десяток вариантов, связанных с порядком вызовов.

Answer 2
 @FXML private javafx.scene.control.Button closeButton; 
//или любая другая нода на сцене

 @FXML 
 private void initialize(){
 Stage stage = (Stage) closeButton.getScene().getWindow(); 
 }

И не очень понятно зачем вам метод инициализации с ресурс бандлом, если вы его не используете.

READ ALSO
Как вернуть фокус в начало текста в JTextPane?

Как вернуть фокус в начало текста в JTextPane?

После вывода текста в JTextPane, необходимо вернуть фокус в начало текста, а потом переместить фокус на кнопкуЧтобы долго не объяснять, вот две...

199
Вызов хранимой процедуры при помощи hibernate

Вызов хранимой процедуры при помощи hibernate

В базе данных есть хранимая процедура

238
Spring. Как связать две таблицы по ID

Spring. Как связать две таблицы по ID

Пытался связать поле одной таблицы с id другойПока не до конца понимаю как это вообще сделать

181