How to call functions on the stage in JavaFX's

2019-02-08 21:36发布

I am using javafx along with fxml, so I use the controller for the real coding. I need to do a few operations on the stage, such as getting its x- or y-axis position. I have tried stage.getX() & stage.getY, but they don't work(the stage name is high-lited as the error). How do I use such functions in my controller? I tried doing this in my main file:

 public int locationX = stage.getX();

and

public double locationX = stage.getX();

But it doesn't work, instead makes the whole program one big error.
So how do I get to do such functions in my controller file? Do I need to import something or do something like above in another way?

error: cannot find symbol
    locationX = stage.getX();
symbol:   variable stage
location: class FXMLController


I know that the "stage" is missing. But how to get the "stage" in my controller?

6条回答
疯言疯语
2楼-- · 2019-02-08 22:20

Sample Solution

You can initialize the stage in the controller using the technique from: Passing Parameters JavaFX FXML.

Here is a sample program which creates a utility window which tracks the x and y co-ordinates of the screen as you drag the utility window around. The contents of the utility window are rendered in an fxml defined pane.

stagecoords

StageTrackingSample.java

import javafx.application.Application;
import javafx.fxml.FXMLLoader;
import javafx.scene.*;
import javafx.stage.*;

public class StageTrackingSample extends Application {

  @Override public void start(final Stage stage) throws Exception {
    final FXMLLoader loader = new FXMLLoader(
      getClass().getResource(
        "stagetracking.fxml"
      )
    );

    final Parent root = (Parent) loader.load();
    final StageTrackingController controller = loader.getController();
    controller.initData(stage);

    stage.initStyle(StageStyle.UTILITY);
    stage.setResizable(false);
    stage.setScene(new Scene(root));
    stage.show();
  }

  public static void main(String[] args) { launch(args); }
}

StageTrackingController.java

import javafx.beans.binding.Bindings;
import javafx.fxml.FXML;
import javafx.scene.control.*;
import javafx.stage.Stage;

public class StageTrackingController {
  @FXML private Label stageX;
  public void initialize() {}  
  public void initData(final Stage stage) {
    stageX.textProperty().bind(
      Bindings.format(
        "(%1$.2f, %2$.2f)", 
        stage.xProperty(), 
        stage.yProperty()
      )
    );
  }
}

stagetracking.fxml

<?xml version="1.0" encoding="UTF-8"?>

<?import javafx.scene.*?>
<?import javafx.scene.control.*?>
<?import javafx.scene.layout.*?>

<AnchorPane id="AnchorPane" maxHeight="-Infinity" maxWidth="-Infinity" minHeight="30" minWidth="100" xmlns:fx="http://javafx.com/fxml" fx:controller="test.StageTrackingController">
  <Label fx:id="stageX" layoutX="0" layoutY="0"/>
</AnchorPane>

Alternate Solutions

tarrsalah's answer of just getting the stage from an @FXML component is also a good way if you know that the root component of the controller has already been added to a scene which has already been added to a stage (which is often the case when something like a button event handler is fired).

Another way to do it is similar to tarrsalah's answer, but to use ChangeListeners on the scene property of an @FXML node and the window property of the changed scene. This allows you to track changes to the scene and stage in case the pane is moved to a new scene or stage. Most of the time you don't need to track those changes though as most panes are just added to a single scene which stays on one stage.

Answers to Additional Questions and Comments

Can I get a simpler answer?

tarrsalah already provided a simpler answer.

The only problem with a simpler answer in this case is that it might not provide enough context required for you to replicate the answer's solution and adapt it to your work.

I made my current answer as simple as I could, but, unfortunately, even the most basic JavaFX FXML application requires quite a bit code and markup to work.

I am a mere beginner in java

Don't use FXML when you are first starting to develop your initial Java and JavaFX applications. Instead, just stick with the standard Java API in your JavaFX code, for which there are many more tutorials as well as the excellent Ensemble Sample to refer to.

Make sure before beginning JavaFX, that you have completed all of the Java Tutorial Trails Covering the Basics. Only the basics of Java are required to start using JavaFX, you don't need to branch off into learning Java Enterprise Edition and can forget about Swing.

Consider using SceneBuilder and FXML for larger applications once you have written a few basic JavaFX applications, hand-coded some layouts according to the Java API and reached a level of comfort with the core technologies. At that time you will likely find that learning FXML is quite straightforward. FXML attributes and elements are just a reflection of the Java APIs.

please explain the other-than-usual bits of your code

I can't really do that as I don't know what is unusual for you.

If there are particular parts of the code that you cannot understand through your own knowledge or research, create a new StackOverflow question for each difficult concept.

查看更多
Luminary・发光体
3楼-- · 2019-02-08 22:21

The best approach is to create a new controller and pass the stage via the constructor (don't use fx:controller on FXML file), otherwise the stage will be null when initialize() is invoked (At that moment of time, when load() invokes initialize(), no stage is attached to the scene, hence the scene's stage is null).

public class AppEntryPoint extends Application
{
    private FXMLLoader loader;

    @Override
    public void init() throws Exception
    {
        loader = new FXMLLoader(getClass().getResource("path-to-fxml-file"));
    }

    @Override
    public void start(Stage initStage) throws Exception
    {
        MyController controller = new MyController(initStage);
        loader.setController(controller);
        Scene scene = loader.load();
        initStage.setScene(scene);
        initStage.show();
    }

    public static void main(String[] args)
    {
        launch(args);
    }
}
public class MyController
{
    private Stage stage;

    @FXML private URL location;
    @FXML private ResourceBundle resources;

    public MyController(Stage stage)
    {
        this.stage = stage;
    }

    @FXML
    private void initialize()
    {
        // You have access to the stage right here
    }
}
查看更多
祖国的老花朵
4楼-- · 2019-02-08 22:25

This thread is old, but I discovered something pertinent to it quite by accident. What I thought was a coding error that shouldn't have worked, yet it did. In the controller class, simply declaring the following member variable:

@FXML private Stage stage;

gave me access to the stage in the controller, much like I have access to a widget in the fxml document. I have not found any documentation that this is the case, but I'll admit I am noob to JavaFX (although an old hand at Swing). But it seems to work. Maybe it's dangerous to count on it though?

查看更多
Lonely孤独者°
5楼-- · 2019-02-08 22:28

With *.FXML files and Controllers and not with Main or Events , use this :

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

element, can be any control or element in you FXML, in my case is an AnchorPane:

@FXML  private AnchorPane element;
查看更多
Root(大扎)
6楼-- · 2019-02-08 22:34

Well, the simplest answer to that...

In your Main class create an instance (an object) of your Controller class:



    FXMLLoader loader = new FXMLLoader(getClass().getResource("Example.fxml"));
    MyController controller = loader.getController();
    controller.setStage(this.stage);

In your Controller class you should put a method of "extraction" (a setter):



    private Stage primaryStage;

    public void setStage(Stage stage) {
         this.primaryStage = stage;
    }

And then you can add a fullscreen button ;)



    @FXML
    private Button btnFullScreen = new Button();

    public void setFullscreen(ActionEvent event){       
         if(primaryStage.isFullScreen()){
              primaryStage.setFullScreen(false);
         } else {
              primaryStage.setFullScreen(true);
         }
    }

查看更多
Lonely孤独者°
7楼-- · 2019-02-08 22:36

From your root Pane in the fxml file :

@FXML
Parent root

You can get the stage from it by:

Stage stage = (Stage) root.getScene().getWindow()

You have a reference to your stage, you can do what you want.

查看更多
登录 后发表回答