Wie können in Java Bilddateien geladen werden?
Das Ausgangsformat für die überwiegende Arbeit mit Bildern
unter AWT
und Swing
ist das java.awt.image.BufferedImage
,
das die Klasse java.awt.Image
erweitert. Ausgehend von
diesem Typ kann ein Bild dann je nach Verwendungszweck in weitere
Bildtypen wie ImageIcon
, RenderedImage
,
etc. gewandelt werden.
Bei der Verwendung von JavaFX
ist die entsprechende Klasse javafx.scene.image.Image
,
von der als Spezialisierung das WritableImage
abgeleitet ist.
Vielfach sorgt jedoch die Methode für Verwirrung, mit der eine
Graphikdatei in einem Programm geladen werden kann. Nicht zuletzt
deshalb, weil sich die Verfahren nicht nur nach verwendeter
GUI-Bibliothek, sondern auch nach Programmtyp (Applet, Application)
unterscheiden.
Hier sollen die Verfahren für
Java-Desktop-Applikationen mit Swing und JavaFX dargestellt werden.
Voraussetzung sind grundlegende Kenntnisse über die Angabe von
Pfaden.
Swing
Das Demo-Programm
Um die verschiedenen Lademethoden zu demonstrieren dient eine
einfache GUI, die innerhalb eines JFrame
und eines JPanel
ein JLabel
lädt, auf dem das geladene Bild als ImageIcon
dargestellt wird. Das Laden selbst und die Konvertierung des BufferedImage
in ein ImageIcon
geschehen in der Methode showImg()
.
Sie wird im Konstruktor des Programms bei der Erzeugung des
Label-Objektes aufgerufen und gibt das ImageIcon
-Objekt
oder im Fehlerfall null
zurück.
Das Wandeln
des BufferedImage
in ein ImageIcon
erfolgt durch dessen Übergabe an den Konstruktor des ImageIcon
.
Die im Folgenden aufgezeigten Routinen zum Laden des Bildes
müssen an Stelle des Kommentars /* Hier Graphikdatei
laden */
eingefügt werden. Die zugehörigen import
-Anweisungen
sind bereits vorhanden.
package grafik;
import java.awt.image.BufferedImage;
import java.io.File;
import java.io.IOException;
import java.io.InputStream;
import java.net.JarURLConnection;
import java.net.URL;
import javax.imageio.ImageIO;
import javax.swing.ImageIcon;
import javax.swing.JFrame;
import javax.swing.JLabel;
import javax.swing.JPanel;
import javax.swing.SwingUtilities;
public class BildLaden {
public BildLaden() {
init();
}
private void init() {
JLabel label = new JLabel(showImg());
JPanel panel = new JPanel();
panel.add(label);
JFrame frame = new JFrame("Bilddatei laden");
frame.add(panel);
frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
frame.setTitle("Frame-Titel");
frame.setSize(600, 400);
frame.setLocationRelativeTo(null);
frame.setVisible(true);
}
private ImageIcon showImg() {
BufferedImage img = null;
try {
/* hier Graphikdatei laden */
} catch (IOException e) {
e.printStackTrace();
}
return new ImageIcon(img);
}
public static void main(String[] args) {
SwingUtilities.invokeLater(() -> new BildLaden());
}
}
Bilddatei im Klassenpfad
Der Klassenpfad oder Classpath
umfasst eine Liste von
Dateipfaden, in denen die Werkzeuge des JDK
nach zu
verwendenden *.class
-Dateien suchen. Alle
Verzeichnisse und Dateien, die sich in Eclipse innerhalb des src
-Verzeichnisses
eines Projektes befinden, sind in den Klassenpfad eingeschlossen.1 Das gilt im vorliegenden Fall auch
für das Verzeichnis img
und dessen Inhalt.
Die ladende Datei BildLaden.java
liegt im Package grafik
,
das sich auf gleicher Ebene wie img
innerhalb des
Klassenpfades befindet.
Eine Bilddatei kann in diesem Fall auf
die folgenden Weisen geladen werden:
img = ImageIO.read(getClass().getResource("../img/test.jpg"));
Das Laden erfolgt bei diesem Verfahren im Hintergrund mit Hilfe des
sog. ClassLoaders des aktuellen Objektes, an den die Anfrage
delegiert wird.
Die Grundlage stellt hier die mehrfach
überladene statische Methode read()
der Klasse ImageIO
dar. Sie liefert ein BufferedImage
und bekommt im
vorliegenden Beispiel ein URL
-Objekt als Parameter
übergeben. Dies wird von der Methode Class.getRessource()
zurückgegeben. Sie verwendet hier als Parameter den String
eines relativen Pfades. Er muss, wie hier geschehen, ausgehend von
der ladenden Datei angegeben werden. Package-Bestandteile werden
hierbei wie Verzeichnisse behandelt.
Wahlweise kann auch ein absoluter Pfad bezogen auf das src
-Verzeichnis
des Projektes angegeben werden. Er muss dann mit einem
Schrägstrich ('/
') beginnen.
img = ImageIO.read(getClass().getResource("/img/test.jpg"));
Bilddatei außerhalb des Klassenpfades
Befindet sich eine zu ladende Datei nicht im Klassenpfad, so muss ein anderes Verfahren angewandt werden.
Im vorliegenden Fall liegt das Verzeichnis img
mit der
Bilddatei auf gleicher Ebene wie das Verzeichnis src
.
Um eine Bilddatei zu laden, kann hier kein ClassLoader verwendet
werden, da sich das Verzeichnis außerhalb von dessen
Zugriffsbereich befindet. Statt dessen wird der Methode ImageIO.read()
ein File
-Objekt als Parameter übergeben:
img = ImageIO.read(new File("img/test.jpg"));
Zur Erzeugung akzeptiert ein Konstruktor der Klasse File
den Pfad zur Bilddatei als String. Dies kann auf zwei Weisen
erfolgen: Der Pfad kann absolut bezogen auf das Dateisystem oder
aber, wie hier demonstriert, relativ zum Eclipse-Projektordner
angegeben werden (diesmal jedoch ohne führenden
Schrägstrich!). Letzteres funktioniert deshalb, weil Eclipse
das Projektverzeichnis (nicht src
oder bin
!)
als Arbeitsverzeichnis verwendet.
Bilddatei in externer JAR
.
Nicht selten werden Ressourcen eines Programms in externen
JAR-Dateien gespeichert, aus denen sie dann geladen werden
können. Der Zugriff auf eine derartige Datei ist nicht direkt
durch die Klasse ImageIO
möglich, sondern muss
vorher über eine JarURLConnection
durch einen InputStream
erfolgen, der dann weiterverarbeitet werden kann.
Bei diesem Beispiel befindet sich die JAR-Datei mit der
Graphikdatei im Verzeichnis img
, das wiederum nicht im
Klassenpfad liegt, sondern auf der Ebene, auf der sich auch src
befindet. Die Bilddatei test.jpg
ist auf oberster
Ebene innerhalb von img.jar
lokalisiert.
Der JarURLConnection
muss eine URL übergeben
werden, zu der die Verbindung geöffnet wird. Hierbei muss man
insbesondere die Syntax der URL beachten. Der Pfad zur JAR-Datei
selbst kann innerhalb des Projektes wiederum relativ zu diesem oder
aber absolut zum Dateisystem des Rechners angegeben werden. Dem
Pfad muss die Formel jar:file:
als Protokoll
vorangestellt werden. Nach der Extension jar
müssen ein Ausrufezeichen und ein Schrägstrich ('!/'
)
stehen. Danach folgt der Pfad zur Bilddatei innerhalb des
JAR-Archivs.
URL url = new URL("jar:file:img/img.jar!/test.jpg"); JarURLConnection jarConnection = (JarURLConnection) url.openConnection(); jarConnection.setDoOutput(true); InputStream inputStream = jarConnection.getInputStream(); img = ImageIO.read(inputStream); inputStream.close();
Da die Verbindung im vorliegenden Fall für eine Ausgabe
genutzt werden soll, ist es sinnvoll, den boolschen Wert für URLConnection.doOutput
auf true
zu setzen, bevor von der Verbindung ein InputStream
abgeleitet wird. Dieser kann schließlich wiederum per ImageIO.read()
ausgelesen werden.
JavaFX
JavaFX ist zunehmend dabei, Swing als Desktop-Fenster-Bibliothek zu ersetzen, da sich die GUI-Programmierung mit JavaFX u.a. dank der Möglichkeiten der Verwendung von Style-Sheets als hochflexibel erweist. Dies ist selbstverständlich auch mit Änderungen in anderen Bereichen verbunden, z.B. bezüglich des Ladens von Graphiken.
Der wesentliche Unterschied zu Swing ist die Kompensation der
Klasse JavaIO
, die ja zum Swing-Package javax.imageio
gehört. Die Funktionen der überladenen Methoden read()
werden dabei weitgehend durch die Konstruktoren der Klasse javafx.scene.image.Image
übernommen.
Während die Methoden ImageIO.read()
als Parameter entweder Objekte vom Typ InputStream, File
oder URL
übergeben bekommen und Objekte vom Typ BufferedImage
zurückgeben, werden bei JavaFX
Objekte vom Typ javafx.scene.image.Image
direkt erzeugt. Den Konstruktoren werden zu diesem Zweck nur noch
ein InputStream
oder die
String-Repräsentation einer URL, nicht das URL
-Objekt
selbst, übergeben. Die Möglichkeit, ein File
-Objekt
zu verwenden ist nicht mehr vorgesehen.
Das Demo-Programm
Das Demo-Programm zeigt, wie eine Graphikdatei geladen und durch
eine ImageView
direkt dargestellt werden kann. Die
u.a. Lademethoden müssen in dem Bereich eingefügt werden,
der mit /* hier Graphikdatei laden */
gekennzeichnet
ist.
Als Layout-Manager wird hier ein BorderPane
als Node
erzeugt, der der Scene
übergeben wird. Zur
Demonstration der Skalierungsmöglichkeiten eines ImageView
ist die Scene
auf die Größe von 250x150
Pixel gesetzt.
import java.io.File;
import java.io.IOException;
import java.io.InputStream;
import java.net.URL;
import javafx.application.Application;
import javafx.scene.Scene;
import javafx.scene.image.Image;
import javafx.scene.image.ImageView;
import javafx.scene.layout.BorderPane;
import javafx.stage.Stage;
public class BildLadenFX extends Application {
private static final int IMG_HEIGHT = 150;
private static final int IMG_WIDTH = 250;
@Override
public void start(Stage primaryStage) throws Exception {
primaryStage.setTitle("Bild laden bei JavaFX");
Image img = null;
BorderPane root = new BorderPane();
Scene scene = new Scene(root, IMG_WIDTH, IMG_HEIGHT);
/* hier Graphikdatei laden */
ImageView imgView = new ImageView(img);
imgView.setFitHeight(IMG_HEIGHT);
imgView.setPreserveRatio(true);
imgView.setSmooth(true);
root.getChildren().add(imgView);
primaryStage.setScene(scene);
primaryStage.show();
}
public static void main(String[] args) {
launch();
}
}
Zunächst wird über den jeweiligen Konstruktor ein Image
-Objekt
erstellt, das wiederum zur Erzeugung eines ImageView
herangezogen wird. Die Klasse stellt eine Reihe von Methoden
bereit, die der Manipulation der Art der Darstellung eines Image
-Objektes
dienen: Nach der Skalierung unter Beibehaltung der Proportionen,
sowie dem Setzen eines Flags, das die Qualität des
Bildrenderings gegenüber der Geschwindigkeit bevorzugt, wird
das ImageView
dem Scenegraph hinzugefügt.
Bilddatei mit URL laden
Wie unter Swing auch, so kann innerhalb eines Eclipse-Projektes
eine URL verwendet werden, um mittels Classloader eine Graphikdatei
zu laden. Wie oben erwähnt, muss hier jedoch nicht das URL
-Objekt
selbst, sondern dessen String-Repräsentation verwendet werden.
Sie wird durch die Methode toExternalForm()
der Klasse
URL
zurückgegeben.
Der Dateipfad entspricht im
vorliegenden Fall wiederum dem Schema von Bilddateien
innerhalb des Klassenpfades.
img = new Image(getClass().getResource("/img/test.jpg").toExternalForm());
Auch außerhalb des Klassenpfades können nun Bilder
mittels eines übergebenen URL-Strings geladen werden. Lokalen
Pfaden muss hierzu das Protokoll file:
vorangestellt
werden.
img = new Image("file:/Users/joecze/img/test.jpg");
Dateien im Netzwerk werden natürlich auf eine äquivalente Weise referenziert:
img = new Image("https://javabeginners.de/img/javabeginners_240.png");
1) Genau genommen gehört bei getrennter Lagerung
der .java
- und .class
-Dateien nicht das
Verzeichnis src
sondern bin
zum
Klassenpfad. Durch den Eclipse-eigenen Build-Mechanismus werden
Ressourcen jedoch von src
nach bin
kopiert.
Wenn Ihnen javabeginners.de gefällt, freue ich mich über eine Spende an diese gemeinnützigen Organisationen.