callum perks
callum perks

Reputation: 97

FXML access button ID in java

I have given a GridPane a value for the fx:id fied in a fxml file and then accessed thid id in the java code with the getId function and this works fine. However when i tried the same thing with a button, getId returns null. Is what I am trying to do possible and if so how can i do something similar?

This is the fxml file:

<?xml version="1.0" encoding="UTF-8"?>
<?import javafx.scene.control.Button?>
<?import javafx.scene.control.Label?>
<?import javafx.scene.layout.GridPane?>
<?import javafx.scene.layout.HBox?>

<?import javafx.scene.layout.VBox?>

<GridPane alignment="center" hgap="40" vgap="40" xmlns:fx="http://javafx.com/fxml/1"
          fx:controller="Tills.Controllers.MoviePageController">
    <GridPane fx:id="movie1Grid" alignment="center" hgap="10" vgap="10" GridPane.columnIndex="0" GridPane.rowIndex="0">
        <HBox alignment="center" GridPane.columnIndex="0" GridPane.rowIndex="0">
            <Label fx:id="movie1" alignment="center" text="Movie 1"/>
        </HBox>
        <HBox alignment="center" GridPane.columnIndex="0" GridPane.rowIndex="1" spacing="10">
            <VBox alignment="center">
                <Button fx:id="button0" text="17:00" onAction="#submitMovieChoice"/>
                <Label text="test screen"/>
            </VBox>
            <VBox alignment="center">
                <Button fx:id="button1" text="13:15" onAction="#submitMovieChoice"/>
                <Label text="test screen"/>
            </VBox>
            <VBox alignment="center">
                <Button fx:id="button2" text="13:30" onAction="#submitMovieChoice"/>
                <Label text="test screen"/>
            </VBox>
        </HBox>
    </GridPane>

    <GridPane fx:id="movie2Grid" alignment="center" hgap="10" vgap="10" GridPane.columnIndex="0" GridPane.rowIndex="1">
        <HBox alignment="center" GridPane.columnIndex="0" GridPane.rowIndex="0">
            <Label fx:id="movie2" alignment="center" text="Movie 1"/>
        </HBox>
        <HBox alignment="center" GridPane.columnIndex="0" GridPane.rowIndex="1" spacing="10">
            <VBox alignment="center">
                <Button fx:id="button3" text="13:00" onAction="#submitMovieChoice"/>
                <Label text="test screen"/>
            </VBox>
            <VBox alignment="center">
                <Button fx:id="button4" text="13:15" onAction="#submitMovieChoice"/>
                <Label text="test screen"/>
            </VBox>
            <VBox alignment="center">
                <Button fx:id="button5" text="13:30" onAction="#submitMovieChoice"/>
                <Label text="test screen"/>
            </VBox>
        </HBox>
    </GridPane>

    <GridPane fx:id="movie3Grid" alignment="center" hgap="10" vgap="10" GridPane.columnIndex="0" GridPane.rowIndex="2">
        <HBox alignment="center" GridPane.columnIndex="0" GridPane.rowIndex="0">
            <Label fx:id="movie3" alignment="center" text="Movie 1"/>
        </HBox>
        <HBox alignment="center" GridPane.columnIndex="0" GridPane.rowIndex="1" spacing="10">
            <VBox alignment="center">
                <Button fx:id="button6" text="13:00" onAction="#submitMovieChoice"/>
                <Label text="test screen"/>
            </VBox>
            <VBox alignment="center">
                <Button fx:id="button7" text="13:15" onAction="#submitMovieChoice"/>
                <Label text="test screen"/>
            </VBox>
            <VBox alignment="center">
                <Button fx:id="button8" text="13:30" onAction="#submitMovieChoice"/>
                <Label text="test screen"/>
            </VBox>
        </HBox>
    </GridPane>

    <GridPane fx:id="movie4Grid" alignment="center" hgap="10" vgap="10" GridPane.columnIndex="1" GridPane.rowIndex="0">
        <HBox alignment="center" GridPane.columnIndex="0" GridPane.rowIndex="0">
            <Label fx:id="movie4" alignment="center" text="Movie 1"/>
        </HBox>
        <HBox alignment="center" GridPane.columnIndex="0" GridPane.rowIndex="1" spacing="10">
            <VBox alignment="center">
                <Button fx:id="button9" text="13:00" onAction="#submitMovieChoice"/>
                <Label text="test screen"/>
            </VBox>
            <VBox alignment="center">
                <Button fx:id="button10" text="13:15" onAction="#submitMovieChoice"/>
                <Label text="test screen"/>
            </VBox>
            <VBox alignment="center">
                <Button fx:id="button11" text="13:30" onAction="#submitMovieChoice"/>
                <Label text="test screen"/>
            </VBox>
        </HBox>
    </GridPane>

    <GridPane fx:id="movie5Grid" alignment="center" hgap="10" vgap="10" GridPane.columnIndex="1" GridPane.rowIndex="1">
        <HBox alignment="center" GridPane.columnIndex="0" GridPane.rowIndex="0">
            <Label fx:id="movie5" alignment="center" text="Movie 1"/>
        </HBox>
        <HBox alignment="center" GridPane.columnIndex="0" GridPane.rowIndex="1" spacing="10">
            <VBox alignment="center">
                <Button fx:id="button12" text="13:00" onAction="#submitMovieChoice"/>
                <Label text="test screen"/>
            </VBox>
            <VBox alignment="center">
                <Button fx:id="button13" text="13:15" onAction="#submitMovieChoice"/>
                <Label text="test screen"/>
            </VBox>
            <VBox alignment="center">
                <Button fx:id="button14" text="13:30" onAction="#submitMovieChoice"/>
                <Label text="test screen"/>
            </VBox>
        </HBox>
    </GridPane>

    <GridPane fx:id="movie6Grid" alignment="center" hgap="10" vgap="10" GridPane.columnIndex="1" GridPane.rowIndex="2">
        <HBox alignment="center" GridPane.columnIndex="0" GridPane.rowIndex="0">
            <Label fx:id="movie6" alignment="center" text="Movie 1"/>
        </HBox>
        <HBox alignment="center" GridPane.columnIndex="0" GridPane.rowIndex="1" spacing="10">
            <VBox alignment="center">
                <Button fx:id="button15" text="13:00" onAction="#submitMovieChoice"/>
                <Label text="test screen"/>
            </VBox>
            <VBox alignment="center">
                <Button fx:id="button16" text="13:15" onAction="#submitMovieChoice"/>
                <Label text="test screen"/>
            </VBox>
            <VBox alignment="center">
                <Button fx:id="button17" text="13:30" onAction="#submitMovieChoice"/>
                <Label text="test screen"/>
            </VBox>
        </HBox>
    </GridPane>
</GridPane>

This is the controller method that tries to get the id:

    public void submitMovieChoice(ActionEvent event) {
        Button button = (Button) event.getSource();
        GridPane movie = (GridPane) button.getParent().getParent().getParent();
        String name = null;

//        retrieve the name of the selected movie
        HBox hbox = (HBox) movie.getChildren().get(0);
        Label label = (Label) hbox.getChildren().get(0);
        name = label.getText();

        //CALLUM THIS IS WHERE THE SCREENING IS NEEDED FOR PASSING THROUGH
        int screeningID = 0;
        int age = 0;

        if(button.getId().length() == 7) {
            screeningID = screeningIDs[Integer.parseInt(button.getId().substring(button.getId().length() - 1))];
        }
        else
            screeningID = screeningIDs[Integer.parseInt(button.getId().substring(button.getId().length() - 2, button.getId().length() - 1 ))];
        try {
            //Load the ticket page with the selected name and time
            FXMLLoader loader = new FXMLLoader(getClass().getResource("../ticketType.fxml"));
            TicketPageController controller = new TicketPageController(button.getText(), name, screeningID, age);
            loader.setController(controller);
            Parent parent = loader.load();
            Stage window = (Stage) ((Node) event.getSource()).getScene().getWindow();
            window.setScene(new Scene(parent));
            window.show();
        } catch (IOException e) {
            System.err.println("Could not load page");
        }
    }

Upvotes: 0

Views: 3224

Answers (1)

James_D
James_D

Reputation: 209340

To answer your question directly: the FXMLLoader will set the id of any node to be the same as its fx:id if no id is explicitly set. So in the code you posted, the buttons will have id of "button0" through button17, as expected.

I simplified your code so I could run it, as follows:

application/Test.fxml (the only change here is to the controller name, as I just put everything into a different package):

<?xml version="1.0" encoding="UTF-8"?>
<?import javafx.scene.control.Button?>
<?import javafx.scene.control.Label?>
<?import javafx.scene.layout.GridPane?>
<?import javafx.scene.layout.HBox?>

<?import javafx.scene.layout.VBox?>

<GridPane alignment="center" hgap="40" vgap="40" xmlns:fx="http://javafx.com/fxml/1"
          fx:controller="application.MoviePageController">
    <GridPane fx:id="movie1Grid" alignment="center" hgap="10" vgap="10" GridPane.columnIndex="0" GridPane.rowIndex="0">
        <HBox alignment="center" GridPane.columnIndex="0" GridPane.rowIndex="0">
            <Label fx:id="movie1" alignment="center" text="Movie 1"/>
        </HBox>
        <HBox alignment="center" GridPane.columnIndex="0" GridPane.rowIndex="1" spacing="10">
            <VBox alignment="center">
                <Button fx:id="button0" text="17:00" onAction="#submitMovieChoice"/>
                <Label text="test screen"/>
            </VBox>
            <VBox alignment="center">
                <Button fx:id="button1" text="13:15" onAction="#submitMovieChoice"/>
                <Label text="test screen"/>
            </VBox>
            <VBox alignment="center">
                <Button fx:id="button2" text="13:30" onAction="#submitMovieChoice"/>
                <Label text="test screen"/>
            </VBox>
        </HBox>
    </GridPane>

    <GridPane fx:id="movie2Grid" alignment="center" hgap="10" vgap="10" GridPane.columnIndex="0" GridPane.rowIndex="1">
        <HBox alignment="center" GridPane.columnIndex="0" GridPane.rowIndex="0">
            <Label fx:id="movie2" alignment="center" text="Movie 2"/>
        </HBox>
        <HBox alignment="center" GridPane.columnIndex="0" GridPane.rowIndex="1" spacing="10">
            <VBox alignment="center">
                <Button fx:id="button3" text="13:00" onAction="#submitMovieChoice"/>
                <Label text="test screen"/>
            </VBox>
            <VBox alignment="center">
                <Button fx:id="button4" text="13:15" onAction="#submitMovieChoice"/>
                <Label text="test screen"/>
            </VBox>
            <VBox alignment="center">
                <Button fx:id="button5" text="13:30" onAction="#submitMovieChoice"/>
                <Label text="test screen"/>
            </VBox>
        </HBox>
    </GridPane>

    <GridPane fx:id="movie3Grid" alignment="center" hgap="10" vgap="10" GridPane.columnIndex="0" GridPane.rowIndex="2">
        <HBox alignment="center" GridPane.columnIndex="0" GridPane.rowIndex="0">
            <Label fx:id="movie3" alignment="center" text="Movie 3"/>
        </HBox>
        <HBox alignment="center" GridPane.columnIndex="0" GridPane.rowIndex="1" spacing="10">
            <VBox alignment="center">
                <Button fx:id="button6" text="13:00" onAction="#submitMovieChoice"/>
                <Label text="test screen"/>
            </VBox>
            <VBox alignment="center">
                <Button fx:id="button7" text="13:15" onAction="#submitMovieChoice"/>
                <Label text="test screen"/>
            </VBox>
            <VBox alignment="center">
                <Button fx:id="button8" text="13:30" onAction="#submitMovieChoice"/>
                <Label text="test screen"/>
            </VBox>
        </HBox>
    </GridPane>

    <GridPane fx:id="movie4Grid" alignment="center" hgap="10" vgap="10" GridPane.columnIndex="1" GridPane.rowIndex="0">
        <HBox alignment="center" GridPane.columnIndex="0" GridPane.rowIndex="0">
            <Label fx:id="movie4" alignment="center" text="Movie 1"/>
        </HBox>
        <HBox alignment="center" GridPane.columnIndex="0" GridPane.rowIndex="1" spacing="10">
            <VBox alignment="center">
                <Button fx:id="button9" text="13:00" onAction="#submitMovieChoice"/>
                <Label text="test screen"/>
            </VBox>
            <VBox alignment="center">
                <Button fx:id="button10" text="13:15" onAction="#submitMovieChoice"/>
                <Label text="test screen"/>
            </VBox>
            <VBox alignment="center">
                <Button fx:id="button11" text="13:30" onAction="#submitMovieChoice"/>
                <Label text="test screen"/>
            </VBox>
        </HBox>
    </GridPane>

    <GridPane fx:id="movie5Grid" alignment="center" hgap="10" vgap="10" GridPane.columnIndex="1" GridPane.rowIndex="1">
        <HBox alignment="center" GridPane.columnIndex="0" GridPane.rowIndex="0">
            <Label fx:id="movie5" alignment="center" text="Movie 1"/>
        </HBox>
        <HBox alignment="center" GridPane.columnIndex="0" GridPane.rowIndex="1" spacing="10">
            <VBox alignment="center">
                <Button fx:id="button12" text="13:00" onAction="#submitMovieChoice"/>
                <Label text="test screen"/>
            </VBox>
            <VBox alignment="center">
                <Button fx:id="button13" text="13:15" onAction="#submitMovieChoice"/>
                <Label text="test screen"/>
            </VBox>
            <VBox alignment="center">
                <Button fx:id="button14" text="13:30" onAction="#submitMovieChoice"/>
                <Label text="test screen"/>
            </VBox>
        </HBox>
    </GridPane>

    <GridPane fx:id="movie6Grid" alignment="center" hgap="10" vgap="10" GridPane.columnIndex="1" GridPane.rowIndex="2">
        <HBox alignment="center" GridPane.columnIndex="0" GridPane.rowIndex="0">
            <Label fx:id="movie6" alignment="center" text="Movie 1"/>
        </HBox>
        <HBox alignment="center" GridPane.columnIndex="0" GridPane.rowIndex="1" spacing="10">
            <VBox alignment="center">
                <Button fx:id="button15" text="13:00" onAction="#submitMovieChoice"/>
                <Label text="test screen"/>
            </VBox>
            <VBox alignment="center">
                <Button fx:id="button16" text="13:15" onAction="#submitMovieChoice"/>
                <Label text="test screen"/>
            </VBox>
            <VBox alignment="center">
                <Button fx:id="button17" text="13:30" onAction="#submitMovieChoice"/>
                <Label text="test screen"/>
            </VBox>
        </HBox>
    </GridPane>
</GridPane>

application/MoviePageController.java (simplified with other dependencies removed):

package application;

import javafx.event.ActionEvent;
import javafx.scene.control.Button;
import javafx.scene.control.Label;
import javafx.scene.layout.GridPane;
import javafx.scene.layout.HBox;

public class MoviePageController {

    public void submitMovieChoice(ActionEvent event) {
        Button button = (Button) event.getSource();
        GridPane movie = (GridPane) button.getParent().getParent().getParent();
        String name = null;

//        retrieve the name of the selected movie
        HBox hbox = (HBox) movie.getChildren().get(0);
        Label label = (Label) hbox.getChildren().get(0);
        name = label.getText();

        System.out.println(name);

        //CALLUM THIS IS WHERE THE SCREENING IS NEEDED FOR PASSING THROUGH
        int screeningID = 0;
        int age = 0;

        if(button.getId().length() == 7) {
            System.out.println("Button id has length 7");
        }
        else
            System.out.println("Button id does not have length 7");

    }

}

and a test case:

application/Test.java

package application;

import javafx.application.Application;
import javafx.fxml.FXMLLoader;
import javafx.scene.Scene;
import javafx.stage.Stage;

public class Test extends Application {

    @Override
    public void start(Stage primaryStage) throws Exception {
        Scene scene = new Scene(FXMLLoader.load(getClass().getResource("Test.fxml")));
        primaryStage.setScene(scene);
        primaryStage.show();
    }

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

This all worked exactly as expected and printed out the appropriate messages, etc.


I just don't recommend your approach at all. It introduces all sorts of additional string binding which is vulnerable to unchecked (by the compiler) typos, as well as having a huge amount of repetitive code. Additionally, you are storing data in the UI elements (e.g. the only place you seem to be representing the movie names is in the labels). This is not a good approach: you should keep the data independently of the UI, and consider the UI controls simply views of the data. (Read up on "Model-View-Controller" type architectures for more about this.)

Consider, for example, putting all the repeated FXML into a single FXML file:

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

<?import javafx.scene.control.Button?>
<?import javafx.scene.control.Label?>
<?import javafx.scene.layout.GridPane?>
<?import javafx.scene.layout.HBox?>
<?import javafx.scene.layout.VBox?>


<GridPane xmlns:fx="http://javafx.com/fxml/1" alignment="center" hgap="10" vgap="10" fx:controller="application.MovieController">
    <HBox alignment="center" GridPane.columnIndex="0"
        GridPane.rowIndex="0">
        <Label fx:id="movie" alignment="center" />
    </HBox>
    <HBox alignment="center" GridPane.columnIndex="0"
        GridPane.rowIndex="1" spacing="10">
        <VBox alignment="center">
            <Button fx:id="firstShowingButton" onAction="#submitFirstChoice" />
            <Label text="test screen" />
        </VBox>
        <VBox alignment="center">
            <Button fx:id="secondShowingButton" onAction="#submitSecondChoice" />
            <Label text="test screen" />
        </VBox>
        <VBox alignment="center">
            <Button fx:id="thirdShowingButton" onAction="#submitThirdChoice" />
            <Label text="test screen" />
        </VBox>
    </HBox>

</GridPane>

With a controller with values you can set:

package application;

import java.io.IOException;

import javafx.fxml.FXML;
import javafx.fxml.FXMLLoader;
import javafx.scene.Parent;
import javafx.scene.Scene;
import javafx.scene.control.Button;
import javafx.scene.control.Label;
import javafx.stage.Stage;

public class MovieController {

    private String firstShowing ;
    private String secondShowing ;
    private String thirdShowing ;

    private int baseID ;


    @FXML
    private Label movie ;
    @FXML
    private Button firstShowingButton ;
    @FXML
    private Button secondShowingButton ;
    @FXML
    private Button thirdShowingButton ;


    public void setMovieName(String movieName) {
        movie.setText(movieName);
    }

    public String getMovieName() {
        return movie.getText() ;
    }

    public int getBaseID() {
        return baseID ;
    }

    public void setBaseID(int baseID) {
        this.baseID = baseID ;
    }

    public String getFirstShowing() {
        return firstShowing;
    }
    public void setFirstShowing(String firstShowing) {
        this.firstShowing = firstShowing;
        firstShowingButton.setText(firstShowing);
    }
    public String getSecondShowing() {
        return secondShowing;
    }
    public void setSecondShowing(String secondShowing) {
        this.secondShowing = secondShowing;
        secondShowingButton.setText(secondShowing);
    }
    public String getThirdShowing() {
        return thirdShowing;
    }
    public void setThirdShowing(String thirdShowing) {
        this.thirdShowing = thirdShowing;
        thirdShowingButton.setText(thirdShowing);
    }

    public void setShowings(String firstShowing, String secondShowing, String thirdShowing) {
        setFirstShowing(firstShowing);
        setSecondShowing(secondShowing);
        setThirdShowing(thirdShowing);
    }

    private void submitChoice(String showing, int id) {
        try {

            int screeningID = baseID * 3 + id ;
            int age =  0 ;
            //Load the ticket page with the selected name and time
            FXMLLoader loader = new FXMLLoader(getClass().getResource("../ticketType.fxml"));
            TicketPageController controller = new TicketPageController(showing, getMovieName(), screeningID, age);
            loader.setController(controller);
            Parent parent = loader.load();
            Stage window = (Stage) movie.getScene().getWindow();
            window.setScene(new Scene(parent));
            window.show();
        } catch (IOException e) {
            System.err.println("Could not load page");
        }
    }

    @FXML
    private void submitFirstChoice() {
        submitChoice(firstShowing, 0);
    }

    @FXML
    private void submitSecondChoice() {
        submitChoice(secondShowing, 1);
    }

    @FXML
    private void submitThirdChoice() {
        submitChoice(thirdShowing, 2);
    }
}

Now you can just assemble this in code:

package application;

import javafx.application.Application;
import javafx.fxml.FXMLLoader;
import javafx.geometry.Pos;
import javafx.scene.Parent;
import javafx.scene.Scene;
import javafx.scene.layout.GridPane;
import javafx.stage.Stage;

public class Main extends Application {

    private final String[] times = {"17:00", "13:15", "13:30", "13:00", "13:15", "13:30", 
            "13:00", "13:15", "13:30", "13:00", "13:15", "13:30", "13:00", "13:15", "13:30", "13:00", "13:15", "13:30"} ;

    @Override
    public void start(Stage primaryStage) throws Exception {
        GridPane root = new GridPane();
        root.setAlignment(Pos.CENTER);
        root.setHgap(40);
        root.setVgap(40);

        for (int i = 0 ; i < 6; i++) {
            FXMLLoader loader = new FXMLLoader(getClass().getResource("MovieView.fxml"));
            Parent movieView = loader.load();
            MovieController controller = loader.getController() ;
            controller.setMovieName("Movie "+(i+1));
            controller.setShowings(times[i*3], times[i*3+1], times[i*3+2]);
            root.add(movieView, i / 3, i % 3);
        }

        Scene scene = new Scene(root);
        primaryStage.setScene(scene);
        primaryStage.show();
    }

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

As well as getting rid of all the "scene navigation" (using calls to getParent() and getChildren() to find other controls), which will completely break if you try to change the appearance of the UI, this gets rid of much of the repetitive code (particularly in the FXML). In real life, you're probably looking the data here up in a database, and you could retrieve that into a single object which you pass to all the controllers, etc. There are variations on this approach, see for example <fx:include> or Custom components in the FXML documentation. You could also subdivide further and avoid the repetitive code with the three buttons (though that might be overkill).

Upvotes: 2

Related Questions