6

(DUPLICATE & SOLVED - see answer below)

I'm doing my first steps in JavaFX and it seems quite hard to use the "SceneBuilder". I'm used to Android and the QtCreator. It looks to me that there is accessing the UI components much easier.

Something like findViewById(R.id.btnPushMe); <- Android Code

Actually I got an solution but it is quite uncomfortable to use. This looks as this:

FXMLLoader loader = new FXMLLoader(MainApp.class.getResource("../fmxl/main.fxml"));

AnchorPane pane = loader.load();
System.out.println("panechilds:" + pane.getChildren().size());

BorderPane border = (BorderPane) pane.getChildren().get(0);
System.out.println("borderchilds:" + border.getChildren().size());

the xml..

<AnchorPane fx:id="mAnchor" maxHeight="-Infinity" maxWidth="-Infinity" minHeight="-Infinity" minWidth="-Infinity" prefHeight="600.0" prefWidth="800.0"
            xmlns="http://javafx.com/javafx/8" xmlns:fx="http://javafx.com/fxml/1" fx:controller="app.progui.MainController">
    <children>
        <BorderPane layoutX="-1.0" prefHeight="600.0" prefWidth="800.0">
            <top>

               ...

Thanks in advance Martin

Edit:

This is a duplicate question (but I will not delete it, because I took some time to find the answer - maybe because JavaFX wasn't asked as much as Android questions were..)

AnchorPane anchor = (AnchorPane) scene.lookup("#mAnchor");

found here: How to find an element with an ID in JavaFX?

Community
  • 1
  • 1
Martin Pfeffer
  • 12,471
  • 9
  • 59
  • 68
  • With FXML, you will be defining instance fields in the controller for the UI elements of interest to you. Those fields, which are initialized during the loading of the controller class instance, will contain references to the GUI objects you wish to use. – scottb Apr 05 '15 at 23:18
  • Yes, that's what I read quite often.. But I don't see any advantage of this "coding style". Code which is written like this looks quite confusingly to me.. :-/ Maybe I'm wrong and other programmer will say this is well structured, but I'm not very satisfied using the SceneBuilder to invoke methods etc.. – Martin Pfeffer Apr 05 '15 at 23:23
  • The `lookup` approach is highly non-robust. Lookups are only available after CSS is applied, which is (usually) after the node has been displayed (sometimes a frame later). Use the controller approach, or at worst use `FXMLLoader.getNamespace()`. – James_D Apr 05 '15 at 23:38

2 Answers2

6

You should use a controller class and access the UI elements there.

Basically you do:

<AnchorPane fx:id="mAnchor" maxHeight="-Infinity" maxWidth="-Infinity" minHeight="-Infinity" minWidth="-Infinity" prefHeight="600.0" prefWidth="800.0"
            xmlns="http://javafx.com/javafx/8" xmlns:fx="http://javafx.com/fxml/1" fx:controller="app.progui.MainController">
    <children>
        <BorderPane fx:id="border" layoutX="-1.0" prefHeight="600.0" prefWidth="800.0">
            <top>

               ...

And then you can access the fx:id-attributed elements in the controller with

package app.progui ;

// ...

public class MainController {

    @FXML
    private BorderPane border ;


    public void initialize() {
        border.setStyle("-fx-background-color: antiquewhite;");
        // ...
    }

    // ...
}

The field names in the controller class must match the fx:id values in the FXML file.

It is possible to access the fx:id-attributed elements in the class that invoked the FXMLLoader, but if you need to do this it is usually a sign that your overall design is wrong. You can do:

FXMLLoader loader = new FXMLLoader(MainApp.class.getResource("../fmxl/main.fxml"));

AnchorPane pane = loader.load();
Map<String, Object> fxmlNamespace = loader.getNamespace();
BorderPane border = (BorderPane) fxmlNamespace.get("border");

assuming the fx:id defined in the FXML snipped above.

James_D
  • 201,275
  • 16
  • 291
  • 322
4

When you design for FXML, you typically design three things: the application logic, the GUI controller logic, and the FXML.

References to the UI controls you wish to access are injected by the FXML loader into your controller class during its loading and initialization so that you do not need to use a FindById() method.

A controller class looks similar to this:

class DCServRecEditor extends DialogController {


@FXML // ResourceBundle that was given to the FXMLLoader
private ResourceBundle resources;

@FXML // URL location of the FXML file that was given to the FXMLLoader
private URL location;

@FXML // fx:id="ancMatchSelector"
private AnchorPane ancMatchSelector; // Value injected by FXMLLoader

@FXML // fx:id="ancServEditor"
private AnchorPane ancServEditor; // Value injected by FXMLLoader

@FXML // fx:id="ancServRecEditor"
private AnchorPane ancServRecEditor; // Value injected by FXMLLoader
:
:

The FXML loading facility automatically injects references into instance fields that are annotated with the @FXML tag. To manipulate a UI control, just access its methods using the appropriate reference.

Separating the UI control logic from your application logic is highly desirable, and effects a "separation of concerns". When you get used to designing FXML UI's this way, you'll enjoy it.

scottb
  • 9,908
  • 3
  • 40
  • 56
  • I know... @FXML makes the field accessable so it don't need to be public. However, if I set up the initialize() method it is marked out (as "unsed" in IntelliJ) - is this normal? Do you know any good tutorial where to get information about the "lifecycle" or better "how to pass objects trough JavaFX?" I really don't feel familiar with it. – Martin Pfeffer Apr 05 '15 at 23:35