JavaFX : 초기화 중에 컨트롤러에서 스테이지를 가져 오는 방법은 무엇입니까?


91

컨트롤러 클래스에서 스테이지 이벤트 (즉, 숨기기)를 처리하고 싶습니다. 그래서 제가해야 할 일은 리스너를 추가하는 것입니다.

((Stage)myPane.getScene().getWindow()).setOn*whatIwant*(...);

하지만 문제는 초기화가

Parent root = FXMLLoader.load(getClass().getResource("MyGui.fxml"));

그리고 전에

Scene scene = new Scene(root);
stage.setScene(scene);

따라서 .getScene ()은 null을 반환합니다.

내가 찾은 유일한 해결 방법은 myPane.sceneProperty ()에 리스너를 추가하는 것입니다. 그리고 그것이 null이 아닐 때 씬이 생기면 .windowProperty () my! goddamn! 마침내 단계를 검색하는 리스너 처리. 그리고 그것은 모두 이벤트를 준비하기 위해 원하는 리스너를 설정하는 것으로 끝납니다. 듣는 사람이 너무 많다고 생각합니다. 내 문제를 해결하는 유일한 방법입니까?

답변:


119

FXMLLoader를 통해 초기화 후 컨트롤러의 인스턴스를 가져올 수 getController()있지만 FXMLLoader정적 메서드를 사용하는 대신를 인스턴스화해야합니다 .

load()나중에 컨트롤러에 직접 호출 한 후 스테이지를 전달합니다 .

FXMLLoader loader = new FXMLLoader(getClass().getResource("MyGui.fxml"));
Parent root = (Parent)loader.load();
MyController controller = (MyController)loader.getController();
controller.setStageAndSetupListeners(stage); // or what you want to do

1
캐스트를 놓치고 있다고 생각하십니까? 부모 루트 = (Parent) loader.load ();
Paul Eden

12
이것이 정말로 이것을 수행하는 가장 좋은 방법입니까? 이것을 보관하기 위해 JavaFX 프레임 워크에서 제공하는 것이 없습니까?
Hannes

1
어떤 이유로 매개 변수없이 생성자를 사용하고 URL을 load()메서드에 전달 하면 작동하지 않습니다 . (또한 javadoc on getController은 켜져 있어야 하는 것처럼 들립니다 setController.)
Bombe

4
@Bombe URL 매개 변수가있는 load () 메서드는 호출하는 인스턴스에 아무것도 설정하지 않는 정적 메서드이기 때문입니다.
zhujik

@Sebastian, 아야, 참으로. 내 잘못이야.
Bombe

112

필요한 것은 AnchorPaneID 를 제공하는 것뿐입니다. 그러면 ID를 얻을 수 있습니다 Stage.

@FXML private AnchorPane ap;
Stage stage = (Stage) ap.getScene().getWindow();

여기에서 Listener필요한 것을 추가 할 수 있습니다 .

편집 : 아래 EarthMind에서 언급했듯이 AnchorPane요소 일 필요는 없습니다 . 정의한 모든 요소가 될 수 있습니다.


2
짧고 달다. 감사합니다
Sedrick

7
참고 element어떤 요소가 될 수있는 fx:id해당 창에서 : Stage stage = (Stage) element.getScene().getWindow();. 예를 들어 창에 Buttonwith 만있는 경우이를 fx:id사용하여 무대를 가져옵니다.
EarthMind

29
getScene()여전히 반환합니다 null.
m0skit0

3
이것이 답입니다, 깔끔합니다!
destan 2016 년

12
초기화가 완료되기 전에 (예 : 컨트롤러 초기화 메서드에서) getScene ()이 null을 반환하기 때문에 작동하지 않습니다. 원래 포스터는 이것이 그의 상황임을 암시합니다. 이 경우 아래 Utku Özdemir가 제시 한 대안 1이 더 좋습니다. 스테이지가 필요하지 않은 경우 한 리스너로 충분합니다. initialize ()에서 이것을 사용하여 ImageView 스트레치를 만듭니다.bgimage.sceneProperty().addListener((observableScene, oldScene, newScene) -> { if (oldScene == null && newScene != null) { bgimage.fitWidthProperty().bind(newScene.widthProperty()); ... } });
Georgie

32

나는 그것이 당신이 원하는 대답이 아니라는 것을 알고 있지만 IMO에서 제안 된 해결책은 좋지 않습니다. 왜? 애플리케이션 상태에 따라 달라지기 때문입니다. JavaFX에서 컨트롤, 장면 및 스테이지는 서로 의존하지 않습니다. 즉, 컨트롤은 장면에 추가하지 않고도 살 수 있으며 장면은 무대에 연결하지 않고도 존재할 수 있습니다. 그런 다음 순간 t1에서 제어가 장면에 연결될 수 있고 순간 t2에서 해당 장면이 무대에 추가 될 수 있습니다.

따라서 컨트롤러 참조를 가져오고 메서드를 호출하고 스테이지를 전달하여 애플리케이션에 상태를 추가하는 방법을 제안합니다. 즉, 스테이지가 생성 된 직후 적절한 순간에 해당 메서드를 호출해야합니다. 즉, 지금 주문을 따라야합니다. 1- 단계 생성 2- 생성 된 단계를 메서드를 통해 컨트롤러에 전달합니다.

이 접근 방식에서는이 순서를 변경할 수 없거나 변경해서는 안됩니다. 그래서 무국적 상태를 잃었습니다. 그리고 소프트웨어에서 일반적으로 상태는 악합니다. 이상적으로 메서드는 호출 순서를 요구하지 않아야합니다.

그렇다면 올바른 솔루션은 무엇입니까? 두 가지 대안이 있습니다.

1- 당신의 접근 방식, 컨트롤러 청취 속성에서 무대를 얻습니다. 이것이 올바른 접근 방식이라고 생각합니다. 이렇게 :

pane.sceneProperty().addListener((observableScene, oldScene, newScene) -> {
    if (oldScene == null && newScene != null) {
        // scene is set for the first time. Now its the time to listen stage changes.
        newScene.windowProperty().addListener((observableWindow, oldWindow, newWindow) -> {
            if (oldWindow == null && newWindow != null) {
                // stage is set. now is the right time to do whatever we need to the stage in the controller.
                ((Stage) newWindow).maximizedProperty().addListener((a, b, c) -> {
                    if (c) {
                        System.out.println("I am maximized!");
                    }
                });
            }
        });
    }
});

2- 당신은 당신이 만드는 곳에서 당신이해야 할 일을합니다 Stage(그리고 그것은 당신이 원하는 것이 아닙니다).

Stage stage = new Stage();
stage.maximizedProperty().addListener((a, b, c) -> {
            if (c) {
                System.out.println("I am maximized!");
            }
        });
stage.setScene(someScene);
...

1
이것은 지구상에서 가장 짧은 JavaFX 참조입니다 !! 고마워, Utku!
Aram Paronikyan 2015-07-30

그것을 보는 흥미로운 방법. 감사합니다 :)
Utku Özdemir

이 접근 방식을 사용하여 TableView를 채우고 TableView를 중심으로 ProgressIndicator를 표시하려고 시도하고 있지만 getX () 및 getY ()의 이벤트 값 내부가 모든 초기화 후에 사용하는 것과 동일하지 않다는 것을 알았습니다. 프로세스 종료 (버튼의 클릭 이벤트 내부) 장면 및 스테이지, 심지어 스테이지 getX () 및 getY ()는 NaN을 반환합니다.
leobelizquierdo

@leobelizquierdo,이 순간 getY () 문제가 발생했습니다. 해결책을 찾았습니까?
JonasAnon

pane이미 무대에 부착 된 씬에 추가 하면 안되죠? 리스너를 확인하고 스테이지가 여전히 null 인 경우 sceneProperty에만 리스너를 추가 해야하지 stageProperty않습니까? 그렇지 않으면 즉시 필요한 작업을 수행합니까?
내일

14

컨트롤러에서 스테이지 객체를 얻는 가장 간단한 방법은 다음과 같습니다.

  1. (컨트롤러 클래스에서 스테이지를 설정하는 setter 메소드가 될 것입니다)와 같이 자체 생성 된 컨트롤러 클래스에 추가 메소드를 추가합니다.

    private Stage myStage;
    public void setStage(Stage stage) {
         myStage = stage;
    }
    
  2. 시작 방법에서 컨트롤러 가져 오기 및 단계 설정

    FXMLLoader loader = new FXMLLoader(getClass().getResource("MyFXML.fxml"));
    OwnController controller = loader.getController();
    controller.setStage(this.stage);
    
  3. 이제 컨트롤러에서 스테이지에 액세스 할 수 있습니다.


이것이 저의 승자였습니다. Robert Martin의 답변은 처음에는 효과가 있었지만 오류가 발생하기 시작했습니다 stage.
Dammeul

1

fx : id를 할당 하거나 모든 노드 (anchorpane, button 등)에 변수를 선언합니다. 그런 다음 여기에 이벤트 핸들러를 추가하고 해당 이벤트 핸들러 내에 아래에 주어진 코드를 삽입합니다.

Stage stage = (Stage)((Node)((EventObject) eventVariable).getSource()).getScene().getWindow();

희망, 이것은 당신을 위해 작동합니다 !!


1

Platform.runLater는 초기화가 완료 될 때까지 실행을 방지합니다. 이 경우 창 너비를 조정할 때마다 목록보기를 새로 고치고 싶습니다.

Platform.runLater(() -> {
    ((Stage) listView.getScene().getWindow()).widthProperty().addListener((obs, oldVal, newVal) -> {
        listView.refresh();
    });
});

당신의 경우

Platform.runLater(()->{
    ((Stage)myPane.getScene().getWindow()).setOn*whatIwant*(...);
});

-3

당신과 함께 얻을 수 있습니다 node.getScene당신은에서 호출하지 않는 경우, Platform.runLater결과는 널 값입니다.

null 값의 예 :

node.getScene();

null 값이없는 예 :

Platform.runLater(() -> {
    node.getScene().addEventFilter(KeyEvent.KEY_PRESSED, event -> {
               //your event
     });
});
당사 사이트를 사용함과 동시에 당사의 쿠키 정책개인정보 보호정책을 읽고 이해하였음을 인정하는 것으로 간주합니다.
Licensed under cc by-sa 3.0 with attribution required.