JavaFX: Başlatma sırasında denetleyiciden nasıl aşama alınır?


91

Denetleyici sınıfımdaki sahne olaylarını (ör. Gizleme) işlemek istiyorum. Yani tek yapmam gereken bir dinleyici eklemek

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

ancak sorun, başlatma işleminin hemen ardından başlamasıdır.

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

ve öncesi

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

böylece .getScene () null döndürür.

Kendi kendime bulduğum tek çözüm, myPane.sceneProperty () öğesine bir dinleyici eklemek ve boş olmadığında sahne alıyorum, buna .windowProperty () ekleyin! Lanet olsun! Sonunda sahneyi elde ettiğim dinleyici işleme. Ve olayları sahnelemek için istenen dinleyicileri ayarlamakla biter. Sanırım çok fazla dinleyici var. Sorunumu çözmenin tek yolu bu mu?

Yanıtlar:


119

Denetleyicinin örneğini FXMLLoaderbaşlatmadan sonra aracılığıyla alabilirsiniz getController(), ancak FXMLLoaderdaha sonra statik yöntemleri kullanmak yerine bir örneğini başlatmanız gerekir .

Daha sonra load()doğrudan denetleyiciyi aradıktan sonra sahneyi geçtim :

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
Bir oyuncu kadrosunun eksik olduğunu mu düşünüyorsunuz? Ana kök = (Ana) loader.load ();
Paul Eden

12
Bunu yapmanın en iyi yolu gerçekten bu mu? Bunu arşivlemek için JavaFX çerçevesi tarafından sağlanan hiçbir şey yok mu?
Hannes

1
Yapıcıyı parametreler olmadan kullanırsanız ve load()yöntemin URL'sini verirseniz bazı nedenlerden dolayı bu işe yaramaz . (Ayrıca, getControllerolması gerektiği gibi seslerdeki javadoc setController.)
Bombe

4
@Bombe bunun nedeni, URL parametresine sahip load () yönteminin, çağırdığınız örnekte hiçbir şey ayarlamayan statik bir yöntem olmasıdır.
zhujik

@Sebastian, gerçekten. Benim hatam.
Bombe

112

İhtiyacınız olan tek şey AnchorPanebir kimlik vermek ve sonra ondan öğrenebilirsiniz Stage.

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

Buradan Listenerihtiyacınız olanı ekleyebilirsiniz .

Düzenleme: Aşağıda EarthMind tarafından belirtildiği gibi, AnchorPaneöğe olmak zorunda değildir ; tanımladığınız herhangi bir öğe olabilir.


2
Kısa ve güzel. Teşekkürler
Sedrick

7
Not elementbir ile herhangi bir öğe olabilir fx:ido pencerede: Stage stage = (Stage) element.getScene().getWindow();. Örneğin, pencerenizde yalnızca bir Buttonile bir varsa fx:id, sahneyi almak için bunu kullanın.
EarthMind

29
getScene()hala geri dönüyor null.
m0skit0

3
Cevap bu, harika!
destan

12
Başlatma tamamlanmadan önce (örneğin denetleyici başlatma yönteminde) bu işe yaramaz çünkü getScene () null döndürür. Orijinal poster bunun onun durumu olduğunu ima ediyor. Bu durumda aşağıda Utku Özdemir tarafından verilen 1. alternatif daha iyidir. Sahneye ihtiyacınız yoksa, bir dinleyici yeterlidir, bunu bir ImageView uzatması yapmak için initialize () 'de kullanıyorum:bgimage.sceneProperty().addListener((observableScene, oldScene, newScene) -> { if (oldScene == null && newScene != null) { bgimage.fitWidthProperty().bind(newScene.widthProperty()); ... } });
Georgie

32

İstediğiniz yanıtın bu olmadığını biliyorum, ancak IMO önerilen çözümler iyi değil (ve sizin yönteminiz). Neden? Çünkü uygulama durumuna bağlıdırlar. JavaFX'te bir kontrol, bir sahne ve bir sahne birbirine bağlı değildir. Bu, bir sahneye eklenmeden bir kontrolün yaşayabileceği ve bir sahnenin bir sahneye eklenmeden var olabileceği anlamına gelir. Ve sonra, t1 anında, kontrol bir sahneye bağlanabilir ve t2 anında bu sahne bir aşamaya eklenebilir (ve bu, neden birbirlerinin gözlemlenebilir özellikleri olduklarını açıklar).

Dolayısıyla, denetleyici referansını almayı ve bir yöntemi çağırmayı öneren yaklaşım, aşamayı buna geçirerek uygulamanıza bir durum ekler. Bu, o yöntemi, aşama oluşturulduktan hemen sonra doğru zamanda çalıştırmanız gerektiği anlamına gelir. Başka bir deyişle, şimdi bir sipariş izlemeniz gerekir: 1- Aşamayı oluşturun 2- Oluşturulan bu aşamayı bir yöntemle kontrolöre geçirin.

Bu yaklaşımda bu sıralamayı değiştiremezsiniz (veya değiştirmemelisiniz). Yani vatansızlığı kaybettin. Ve yazılımda genellikle durum kötüdür. İdeal olarak, yöntemler herhangi bir çağrı sırası gerektirmemelidir.

Peki doğru çözüm nedir? İki alternatif var:

1- Yaklaşımınız, kontrolör dinleme özelliklerinde sahneye çıksın. Bunun doğru yaklaşım olduğunu düşünüyorum. Bunun gibi:

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- Yapmanız gereken şeyi, yarattığınız yerde yaparsınız Stage(ve istediğiniz bu değil):

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

1
Bu, gezegendeki en kısa JavaFX referansıdır !! Teşekkürler Utku!
Aram Paronikyan

İlginç bir bakış açısı. Teşekkürler :)
Utku Özdemir

Bu yaklaşımı bir TableView doldurmak ve TableView merkezli bir ProgressIndicator göstermek için kullanmaya çalışıyorum, ancak getX () ve getY () olaylarının içindeki değerlerin tüm başlatmadan sonra kullandığınız gibi aynı olmadığı ortaya çıktı. işlem sonu (bir düğmedeki tıklama olayının içinde) sahne ve sahne için, hatta getX () ve getY () aşaması NaN döndürür, herhangi bir fikriniz var mı?
leobelizquierdo

@leobelizquierdo, şu anda getY () problemini anladınız, bir çözüm buldunuz mu?
JonasAnon

Ben eklerseniz panebu hakkı, çalışma olmaz zaten aşamaya bağlı bir sahne için? scenePropertyDinleyicide bir kontrol olması ve dinleyiciyi yalnızca stagePropertysahne hala boşsa eklemesi gerekmez mi? Aksi takdirde, hemen yapmam gerekeni mi yapacağım?
yarın

14

Controller'da sahne nesnesini almanın en basit yolu şudur:

  1. Kendi oluşturduğunuz denetleyici sınıfına ekstra bir yöntem ekleyin (denetleyici sınıfında aşamayı ayarlamak için bir ayarlayıcı yöntemi olacaktır),

    private Stage myStage;
    public void setStage(Stage stage) {
         myStage = stage;
    }
    
  2. Denetleyiciyi başlangıç ​​yöntemine alın ve aşamayı ayarlayın

    FXMLLoader loader = new FXMLLoader(getClass().getResource("MyFXML.fxml"));
    OwnController controller = loader.getController();
    controller.setStage(this.stage);
    
  3. Artık sahneye denetleyicide erişebilirsiniz


Bu benim için kazanan oldu. Robert Martin'in cevabı ilk başta işe yaradı, ancak sonra stageböyle yaparak çözdüğüm bir hatayı atmaya başladı .
Dammeul

1

Ata fx: id anchorpane, düğme, vb Sonra kendisine verilen kod aşağıda insert o olay işleyicisi içinde olay işleyicisi ekleyin: Herhangi bir düğümün / olarak veya beyan değişken:

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

Umarım bu sizin için çalışır !!


1

Platform.runLater, başlatma tamamlanana kadar yürütmeyi önlemek için çalışır. Bu durumda, pencere genişliğini her yeniden boyutlandırdığımda bir liste görünümünü yenilemek istiyorum.

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

Senin durumunda

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

-2

İle alabilirsiniz node.getScene, eğer arama yapmazsanız Platform.runLater, sonuç boş bir değerdir.

örnek boş değer:

node.getScene();

örnek boş değer yok:

Platform.runLater(() -> {
    node.getScene().addEventFilter(KeyEvent.KEY_PRESSED, event -> {
               //your event
     });
});
Sitemizi kullandığınızda şunları okuyup anladığınızı kabul etmiş olursunuz: Çerez Politikası ve Gizlilik Politikası.
Licensed under cc by-sa 3.0 with attribution required.