Search code examples
javajavafxfxml

For loop in FXML file || How to send data from controller to fxml file?


So I am working on a javaFX application and I want to create multiple <ImageView> using for loop!

Is that possible to make for/foreach loop in a .fxml file ?

If yes , then how ?

Also i have another question! how to send data from the controller to sample.fxml file ? for exemple i want to send a table form controller.java to sample.fxml file and use that table+for loop to make <ImageView> in the fxml file!

Note: I am using the fxml to display the image because I am using those images as buttons.

Here is the code of sample.fxml :

<?import javafx.scene.layout.GridPane?>
<?import javafx.scene.image.ImageView?>
<?import javafx.scene.image.Image?>
<GridPane fx:controller="sample.Controller"
          xmlns:fx="http://javafx.com/fxml" alignment="center" hgap="10" vgap="10">


    <ImageView fitHeight="120" fitWidth="120" fx:id="panda" GridPane.columnIndex="0" GridPane.rowIndex="0" onMousePressed="#mousePressed">
        <image>
            <Image  url="@pics/panda.png">

            </Image>
        </image>
    </ImageView>

</GridPane>

Here is the code of Controller.java :

package sample;

import javafx.scene.input.MouseEvent;
import javafx.scene.media.AudioClip;

public class Controller {

    public void play_audio()
    {
        AudioClip sound = new AudioClip(this.getClass().getResource("voices/panda.mp3").toString());
        sound.play();
    }

    public void mousePressed() {
        play_audio();
    }
}

Code of Main.java :

package sample;

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

public class Main extends Application {

    @Override
    public void start(Stage primaryStage) throws Exception{
        Parent root = FXMLLoader.load(getClass().getResource("sample.fxml"));

        primaryStage.setTitle("Animal Sound");


        Scene scene = new Scene(root, 790, 675);
        scene.getStylesheets().add("sample/styles.css");
        primaryStage.setScene(scene);

        primaryStage.show();
    }


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

Solution

  • I don't think flow control is manageable in an fxml file. Also you don't really pass arguments to an fxml file.

    I think you've separated this well, and a little more paring down should get it to work. I would recommend to specify the sounds and the images from the controller. So I would make the controller for each button.

    package sample;
    import javafx.fxml.FXML;
    import javafx.scene.image.ImageView;
    import javafx.scene.image.Image;
    
    public class AudioButtonController {
        String sound;
        @FXML
        ImageView image;
        public void setAudioLocation(String resourcePath){
            sound = resourcePath;
        }
        public void setImageLocation(String img){
            image.setImage( new Image( img ) );
        }
        public void mousePressed() {
            System.out.println(sound);
        }
    }
    

    Now your fxml for each button can be.

    <?import javafx.scene.image.ImageView?>
    <?import javafx.scene.layout.HBox?>
    <HBox xmlns:fx="http://javafx.com/fxml" fx:controller="sample.AudioButtonController">
      <ImageView 
          fitHeight="120" fitWidth="120" fx:id="image" onMousePressed="#mousePressed">
      </ImageView>
    </HBox>
    

    Here is an example main class that starts up and loads 2 audio buttons, but it could be used to load N buttons.

    package sample;
    
    import javafx.application.Application;
    import javafx.fxml.FXMLLoader;
    import javafx.scene.Parent;
    import javafx.scene.Scene;
    import javafx.scene.layout.FlowPane;
    import javafx.stage.Stage;
    public class Main extends Application{
    
    @Override
        public void start(Stage primaryStage) throws Exception{
            FlowPane root = new FlowPane(5, 5);
    
            primaryStage.setTitle("Animal Sound");
    
            String[] imgs = { "https://conserveblog.files.wordpress.com/2016/05/flagship-panda-thumbnail.jpeg?w=188", "http://news.bbc.co.uk/media/images/38625000/jpg/_38625095_021223panda150.jpg" };
            for(String img: imgs){
                FXMLLoader loader = new FXMLLoader( getClass().getResource("audio_button.fxml") );
                Parent audioButton = loader.load();
                AudioButtonController abc = loader.getController();
                abc.setAudioLocation("not supported");
                abc.setImageLocation(img);
                root.getChildren().add(audioButton);
            }
    
    
    
            Scene scene = new Scene(root, 790, 675);
            primaryStage.setScene(scene);
    
            primaryStage.show();
        }
    
    }
    

    This is a bit cumbersome for this example. If your button had more layout controls to it, and your outer layout was more complicated, then this could save some time.