JavaFX: Bagaimana cara mendapatkan panggung dari pengontrol selama inisialisasi?

91

Saya ingin menangani acara panggung (yaitu bersembunyi) dari kelas pengontrol saya. Jadi yang harus saya lakukan adalah menambahkan pendengar melalui

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

tetapi masalahnya adalah inisialisasi dimulai tepat setelahnya

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

dan sebelumnya

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

jadi .getScene () mengembalikan null.

Satu-satunya solusi yang saya temukan sendiri adalah dengan menambahkan listener ke myPane.sceneProperty (), dan jika sudah tidak null saya mendapatkan scene, tambahkan ke .windowProperty () my! Goddamn! penanganan pendengar yang akhirnya saya ambil tahap. Dan itu semua diakhiri dengan pengaturan pendengar yang diinginkan ke acara panggung. Saya pikir ada terlalu banyak pendengar. Apakah ini satu-satunya cara untuk menyelesaikan masalah saya?

Chechulin
sumber

Jawaban:

119

Anda bisa mendapatkan instance pengontrol dari FXMLLoadersetelah inisialisasi melalui getController(), tetapi Anda perlu membuat instance FXMLLoaderdaripada menggunakan metode statis.

Saya akan melewati tahap setelah memanggil load()langsung ke pengontrol setelahnya:

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
zhujik
sumber
1
Pikirkan Anda kehilangan pemain? Root induk = (Induk) loader.load ();
Paul Eden
12
Apakah ini benar-benar cara terbaik untuk melakukan ini? Tidak ada yang disediakan oleh framework JavaFX untuk mengarsipkan ini?
Hannes
1
Untuk beberapa alasan, ini tidak berfungsi jika Anda menggunakan konstruktor tanpa parameter dan memasukkan URL ke load()metode. (Juga, javadoc di getControllerterdengar seperti seharusnya aktif setController.)
Bombe
4
@Bombe ini karena metode load () dengan parameter URL adalah metode statis yang tidak menyetel apa pun pada instance yang Anda panggil.
zhujik
@Sebastian, aduh ya. Salahku.
Bombe
112

Yang Anda butuhkan hanyalah memberikan AnchorPaneID, dan kemudian Anda bisa mendapatkan Stagedari itu.

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

Dari sini, Anda dapat menambahkan Listeneryang Anda butuhkan.

Sunting: Seperti yang dinyatakan oleh EarthMind di bawah ini, itu tidak harus menjadi AnchorPaneelemen; dapat berupa elemen apa pun yang Anda tentukan.

Robert Martin
sumber
2
Pendek dan manis. Terima kasih
Sedrick
7
Perhatikan bahwa elementdapat setiap elemen dengan fx:iddi jendela bahwa: Stage stage = (Stage) element.getScene().getWindow();. Misalnya, jika Anda hanya memiliki dengan Buttondengan fx:iddi jendela Anda, gunakan itu untuk mendapatkan panggung.
EarthMind
29
getScene()masih kembali null.
m0skit0
3
Inilah jawabannya, rapi!
destan
12
Sebelum inisialisasi selesai (misalnya dalam metode inisialisasi pengontrol), ini tidak akan berfungsi karena getScene () mengembalikan null. Poster asli menyiratkan ini adalah situasinya. Dalam hal ini alternatif 1 yang diberikan oleh Utku Özdemir di bawah ini lebih baik. Jika Anda tidak membutuhkan stage maka satu pendengar sudah cukup, saya menggunakan ini di initialize () untuk membuat peregangan ImageView:bgimage.sceneProperty().addListener((observableScene, oldScene, newScene) -> { if (oldScene == null && newScene != null) { bgimage.fitWidthProperty().bind(newScene.widthProperty()); ... } });
Georgie
32

Saya tahu itu bukan jawaban yang Anda inginkan, tetapi IMO solusi yang diusulkan tidak baik (dan cara Anda sendiri adalah). Mengapa? Karena mereka bergantung pada status aplikasi. Di JavaFX, kontrol, pemandangan, dan panggung tidak bergantung satu sama lain. Artinya, kontrol bisa hidup tanpa ditambahkan ke adegan dan adegan bisa ada tanpa terikat ke panggung. Dan kemudian, pada saat t1, kontrol dapat dilampirkan ke sebuah adegan dan pada saat t2, adegan itu dapat ditambahkan ke sebuah panggung (dan itu menjelaskan mengapa mereka merupakan properti yang dapat diamati satu sama lain).

Jadi, pendekatan yang menyarankan untuk mendapatkan referensi pengontrol dan memanggil metode, meneruskan tahapan ke sana akan menambahkan status ke aplikasi Anda. Ini berarti Anda perlu menjalankan metode itu pada saat yang tepat, tepat setelah tahapan dibuat. Dengan kata lain, Anda harus mengikuti perintah sekarang: 1- Buat dekor 2- Lewati dekor yang dibuat ini ke pengontrol melalui sebuah metode.

Anda tidak dapat (atau tidak seharusnya) mengubah urutan ini dalam pendekatan ini. Jadi Anda kehilangan kewarganegaraan. Dan dalam perangkat lunak, secara umum, negara itu jahat. Idealnya, metode tidak memerlukan urutan panggilan apa pun.

Jadi apa solusi yang tepat? Ada dua alternatif:

1- Pendekatan Anda, dalam properti pengontrol mendengarkan untuk mendapatkan panggung. Saya pikir ini adalah pendekatan yang tepat. Seperti ini:

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- Anda melakukan apa yang perlu Anda lakukan di mana Anda membuat Stage(dan bukan itu yang Anda inginkan):

Stage stage = new Stage();
stage.maximizedProperty().addListener((a, b, c) -> {
            if (c) {
                System.out.println("I am maximized!");
            }
        });
stage.setScene(someScene);
...
Utku Özdemir
sumber
1
Ini adalah referensi JavaFX terpendek di planet ini !! Terima kasih, Utku!
Aram Paronikyan
Cara yang menarik untuk melihatnya. Terima kasih :)
Utku Özdemir
Saya mencoba menggunakan pendekatan ini untuk mengisi TableView dan menunjukkan ProgressIndicator yang berpusat pada TableView tetapi ternyata di dalam nilai peristiwa untuk getX () dan getY () tidak sama seperti jika Anda menggunakan setelah semua inisialisasi proses akhir (di dalam acara klik dalam sebuah tombol) baik untuk adegan dan panggung, bahkan panggung getX () dan getY () mengembalikan NaN, tahu?
leobelizquierdo
@leobelizquierdo, mendapat masalah getY () saat ini, apakah Anda menemukan solusi?
JonasAnon
jika saya menambahkan paneke adegan yang sudah dilampirkan ke panggung, ini tidak akan berhasil, bukan? Bukankah seharusnya ada tanda centang di scenePropertylistener dan hanya menambahkan stagePropertylistener jika stage masih null? Jika tidak, lakukan saja apa yang perlu saya lakukan sekarang juga?
besok
14

Cara termudah untuk mendapatkan objek panggung dalam pengontrol adalah:

  1. Tambahkan metode tambahan di kelas pengontrol yang dibuat sendiri seperti (ini akan menjadi metode penyetel untuk mengatur bidang di kelas pengontrol),

    private Stage myStage;
    public void setStage(Stage stage) {
         myStage = stage;
    }
    
  2. Dapatkan pengontrol dalam metode start dan setel panggung

    FXMLLoader loader = new FXMLLoader(getClass().getResource("MyFXML.fxml"));
    OwnController controller = loader.getController();
    controller.setStage(this.stage);
    
  3. Sekarang Anda dapat mengakses dekor dalam pengontrol

Sandeep Kumar
sumber
Ini adalah pemenang bagi saya. Jawaban Robert Martin berhasil pada awalnya, tetapi kemudian mulai melakukan kesalahan yang saya selesaikan dengan menjadi stageseperti ini.
Dammeul
1

Tetapkan fx: id atau deklarasikan variabel ke / dari sembarang node: anchorpane, button, dll. Kemudian tambahkan event handler padanya dan di dalam event handler itu masukkan kode yang diberikan di bawah ini:

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

Semoga berhasil untuk Anda !!

Ankit RajDeo
sumber
1

Platform.runLater berfungsi untuk mencegah eksekusi hingga inisialisasi selesai. Dalam hal ini, saya ingin menyegarkan tampilan daftar setiap kali saya mengubah ukuran lebar jendela.

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

dalam kasus Anda

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

Anda bisa mendapatkan dengan node.getScene, jika Anda tidak menelepon dari Platform.runLater, hasilnya adalah nilai null.

contoh nilai null:

node.getScene();

contoh tidak ada nilai nol:

Platform.runLater(() -> {
    node.getScene().addEventFilter(KeyEvent.KEY_PRESSED, event -> {
               //your event
     });
});
fjqa86.dll
sumber