Issues with your approach
You don't have a well-formed url
new Image(String url) takes a url as a parameter.
A space is not a valid character for a URL:
which is why your x
string is not a valid URL and cannot be used to construct an image.
You need to provide an input recognized by the Image constructor
Note, that it is slightly more complex because, from the Image javadoc, the url parameter can be somethings other than a straight url, but even still, none of them match what you are trying to lookup.
If a URL string is passed to a constructor, it be any of the
following:
- the name of a resource that can be resolved by the context ClassLoader
for this thread
- a file path that can be resolved by File
- a URL that
can be resolved by URL and for which a protocol handler exists
The RFC
2397 "data" scheme for URLs is supported in addition to the protocol
handlers that are registered for the application. If a URL uses the
"data" scheme, the data must be base64-encoded and the MIME type must
either be empty or a subtype of the image type.
You are assuming the resources are in a file system, but that won't always work
If you pack your resources into a jar, then this will not work:
Arrays.asList(
new File(
Thread.currentThread()
.getContextClassLoader()
.getResource(packageName)
.toURI()
).listFiles()
);
This doesn't work because files in the jar are located using the jar:
protocol rather than the file:
protocol. So, you will be unable to create File objects from the jar:
protocol URIs that will be returned by getResource.
Recommended Approach: Use Spring
Getting a list of resources from a jar is actually a pretty tricky thing. From the question you linked, the easiest solution is the one which uses
Unfortunately, that means requiring a dependency on the Spring framework to use it, which is total overkill for this task . . . however I don't know of any other simple robust solution. But at least you can just call the Spring utility class, you don't need to start up a whole spring dependency injection container to use it, so you don't really need to know any Spring at all or suffer any Spring overhead to do it this way.
So, you could write something like this (ResourceLister is a class I created, as well as the toURL method, see the example app):
public List<String> getResourceUrls(String locationPattern) throws IOException {
ClassLoader classLoader = ResourceLister.class.getClassLoader();
PathMatchingResourcePatternResolver resolver = new PathMatchingResourcePatternResolver(classLoader);
Resource[] resources = resolver.getResources(locationPattern);
return Arrays.stream(resources)
.map(this::toURL)
.filter(Objects::nonNull)
.collect(Collectors.toList());
}
Executable Example
ResourceLister.java
import org.springframework.core.io.Resource;
import org.springframework.core.io.support.PathMatchingResourcePatternResolver;
import java.io.IOException;
import java.util.*;
import java.util.stream.Collectors;
public class ResourceLister {
// currently, only gets pngs, if needed, can add
// other patterns and union the results to get
// multiple image types.
private static final String IMAGE_PATTERN =
"classpath:/img/*.png";
public List<String> getImageUrls() throws IOException {
return getResourceUrls(IMAGE_PATTERN);
}
public List<String> getResourceUrls(String locationPattern) throws IOException {
ClassLoader classLoader = ResourceLister.class.getClassLoader();
PathMatchingResourcePatternResolver resolver = new PathMatchingResourcePatternResolver(classLoader);
Resource[] resources = resolver.getResources(locationPattern);
return Arrays.stream(resources)
.map(this::toURL)
.filter(Objects::nonNull)
.collect(Collectors.toList());
}
private String toURL(Resource r) {
try {
if (r == null) {
return null;
}
return r.getURL().toExternalForm();
} catch (IOException e) {
return null;
}
}
public static void main(String[] args) throws IOException {
ResourceLister lister = new ResourceLister();
System.out.println(lister.getImageUrls());
}
}
AnimalApp.java
import javafx.application.Application;
import javafx.geometry.*;
import javafx.scene.Scene;
import javafx.scene.control.Button;
import javafx.scene.image.*;
import javafx.scene.layout.VBox;
import javafx.stage.Stage;
import java.io.IOException;
import java.util.*;
import java.util.stream.Collectors;
public class AnimalApp extends Application {
private static final double ANIMAL_SIZE = 512;
// remove the magic seed if you want a different random sequence all the time.
private final Random random = new Random(42);
private final ResourceLister resourceLister = new ResourceLister();
private List<Image> images;
@Override
public void init() {
List<String> imageUrls = findImageUrls();
images = imageUrls.stream()
.map(Image::new)
.collect(Collectors.toList());
}
@Override
public void start(Stage stage) {
ImageView animalView = new ImageView();
animalView.setFitWidth(ANIMAL_SIZE);
animalView.setFitHeight(ANIMAL_SIZE);
animalView.setPreserveRatio(true);
Button findAnimalButton = new Button("Find animal");
findAnimalButton.setOnAction(e ->
animalView.setImage(randomImage())
);
VBox layout = new VBox(10,
findAnimalButton,
animalView
);
layout.setPadding(new Insets(10));
layout.setAlignment(Pos.CENTER);
stage.setScene(new Scene(layout));
stage.show();
}
private List<String> findImageUrls() {
try {
return resourceLister.getImageUrls();
} catch (IOException e) {
e.printStackTrace();
}
return new ArrayList<>();
}
/**
* Chooses a random image.
*
* Allows the next random image chosen to be the same as the previous image.
*
* @return a random image or null if no images were found.
*/
private Image randomImage() {
if (images == null || images.isEmpty()) {
return null;
}
return images.get(random.nextInt(images.size()));
}
public static void main(String[] args) {
launch(args);
}
}
pom.xml
<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 https://maven.apache.org/xsd/maven-4.0.0.xsd">
<modelVersion>4.0.0</modelVersion>
<groupId>com.example</groupId>
<artifactId>resource-lister</artifactId>
<version>1.0-SNAPSHOT</version>
<name>resource-lister</name>
<properties>
<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
<junit.version>5.7.1</junit.version>
</properties>
<dependencies>
<dependency>
<groupId>org.openjfx</groupId>
<artifactId>javafx-controls</artifactId>
<version>17.0.2</version>
</dependency>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-core</artifactId>
<version>LATEST</version>
</dependency>
</dependencies>
<build>
<plugins>
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-compiler-plugin</artifactId>
<version>3.8.1</version>
<configuration>
<source>17</source>
<target>17</target>
</configuration>
</plugin>
</plugins>
</build>
</project>
images
Place in src/main/resources/img.
- chicken.png
- cow.png
- pig.png
- sheep.png

execution command
Set the VM arguments for your JavaFX SDK installation:
-p C:\dev\javafx-sdk-17.0.2\lib --add-modules javafx.controls