Search code examples
javacssjavafxfxml

JavaFX FXML Background Image: How to select an image from a folder without knowing exactly its' name?


I am trying to set the background image of a JavaFX program same as the user background.

The background image in windows is found in : "%AppData%\Microsoft\Windows\Themes\CachedFiles"

and using JavaFX i added the style using:

style="-fx-background-image: url('%AppData%\Microsoft\Windows\Themes\CachedFiles\*.jpg');"

what should I replace the '*.jpg' with (knowing that there is 1 photo there only)? or how can I come around this issue?


Solution

  • You can't do it with FXML alone. You will need to set the background from the controller after the FXML is loaded.

    FXML:

    <?xml version="1.0" encoding="UTF-8"?>
    <?import javafx.scene.layout.AnchorPane?>
    <?import javafx.scene.control.Label?>
    <?import javafx.scene.control.TextField?>
    <?import javafx.scene.control.PasswordField?>
    <?import javafx.scene.control.Button?>
    <AnchorPane fx:controller="stackoverflow.answers.demo.Main$Controller" fx:id="mainPane" maxHeight="-Infinity" maxWidth="-Infinity" minHeight="-Infinity" minWidth="-Infinity" prefHeight="400.0" prefWidth="600.0" xmlns="http://javafx.com/javafx/11.0.1" xmlns:fx="http://javafx.com/fxml/1">
       <children>
          <TextField fx:id="username" layoutX="30.0" layoutY="125.0" />
          <Label layoutX="30.0" layoutY="99.0" text="Username" />
          <Label layoutX="30.0" layoutY="174.0" text="Password" />
          <PasswordField fx:id="password" layoutX="30.0" layoutY="200.0" />
          <Button fx:id="login" layoutX="61.0" layoutY="338.0" mnemonicParsing="false" text="Login" />
       </children>
    </AnchorPane>
    

    Code:

    public class Main extends Application {
    
        public static class Controller {
    
            @FXML
            AnchorPane mainPane;
            @FXML
            TextField username;
            @FXML
            PasswordField password;
            @FXML
            Button login;
        }
    
        public static void main(String[] args) {
            launch(args);
        }
    
        private Optional<String> pickRandomImage() {
            File[] imgs =  Paths.get(System.getenv("APPDATA"), "Microsoft", "Windows", "Themes", "CachedFiles")
                 .toFile().listFiles((File f) -> f.getName().endsWith(".jpg"));
            if (imgs != null && imgs.length > 0) {
                Collections.shuffle(Arrays.asList(imgs));
                return Optional.of(imgs[0].toURI().toString());
            }
            return Optional.empty();
        }
    
        @Override
        public void start(Stage stage) {
            stage.setTitle("Random Background");
            FXMLLoader loader = new FXMLLoader(getClass().getResource("/layout.xml"));
            try {
                Parent root = loader.load();
                Controller ctrl = loader.getController();
                pickRandomImage().ifPresent(imgurl -> {
                    Image image = new Image(imgurl);
                    ctrl.mainPane.setBackground(new Background(new BackgroundImage(image, BackgroundRepeat.REPEAT, BackgroundRepeat.REPEAT, BackgroundPosition.CENTER, BackgroundSize.DEFAULT)));
                });
                Scene scene = new Scene(root);
                stage.setScene(scene);
                stage.show();
            } catch (IOException ex) {
                Logger.getLogger(Main.class.getName()).log(Level.SEVERE, null, ex);
            }
        }
    }
    

    Note that my example will pick a random image from the folder if multiple images are present. You can get rid of that part or leave it as it won't effect the result if only one image is present.