Reputation: 1
This is my second java project, I am totally new to programming but I have been working on this for quite a while now. However the code could be a mess. I am attempting to reproduce an Image in a JFrame, but I have no idea how to resolve the stated problem. Could it be the fault of the file format? When I insert the package folder and the image file as a path, it compiles without an error message, even though it seemingly doesn't draw anything. After I changed the path to my Desktop (or really anything else) I get the following exception:
Exception in thread "main" java.lang.IllegalArgumentException: input == null!
at javax.imageio.ImageIO.read(Unknown Source)
at Informatik.Timezones.main(Timezones.java:287)
Apparently the source is not found, which means it was correctly recognized before. Perhaps I am drawing on the JFrame instead of a Label or Panel and this causes the problem.
Here is the part of the code in the main method which generates the Panel and draws the image.
JFrame zeichnen = new JFrame();
face = new JLabel();
face.setVisible(true);
face.setSize(1000, 1000);
face.setLayout(null);
face.setLocation(0, 0);
zeichnen.add(face);
zeichnen.setVisible(true);
zeichnen.setSize(1000, 1000);
zeichnen.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
zeichnen.setLocationRelativeTo(null);
zeichnen.setResizable(false);
zeichnen.setLayout(null);
try {
bildchen = ImageIO.read(Timezones.class.getClassLoader().getResourceAsStream("Info rmatik/a.jpg"));
} catch (IOException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}
This is my reprex:
package Informatik;
import java.awt.Graphics;
import java.awt.Graphics2D;
import java.awt.image.BufferedImage;
import java.io.IOException;
import javax.imageio.ImageIO;
import java.awt.RenderingHints;
import javax.swing.*;
public class Test extends JPanel{
static JLabel face;
static BufferedImage bildchen;
@Override
public void paintComponent(Graphics maler) {
super.paintComponent(maler);
Graphics2D maler2 = (Graphics2D) maler;
maler2.setRenderingHint(RenderingHints.KEY_ANTIALIASING, RenderingHints.VALUE_ANTIALIAS_ON);
maler.drawImage(bildchen, 1000, 1000, null);
}
public static void main (String args []) {
JFrame zeichnen = new JFrame();
face = new JLabel();
face.setVisible(true);
face.setSize(1000, 1000);
face.setLayout(null);
face.setLocation(0, 0);
zeichnen.add(face);
zeichnen.setVisible(true);
zeichnen.setSize(1000, 1000);
try {
bildchen = ImageIO.read(Timezones.class.getClassLoader().getResourceAsStream("/Informatik/a.jpg"));
} catch (IOException e) {
//catch block
e.printStackTrace();
}
zeichnen.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
zeichnen.setLocationRelativeTo(null);
zeichnen.setResizable(false);
zeichnen.setLayout(null);
}
}
Here's the new, working version:
package Informatik;
import java.awt.Color;
import java.awt.Graphics;
import java.awt.image.BufferedImage;
import java.io.IOException;
import javax.imageio.ImageIO;
import javax.print.DocFlavor.URL;
import javax.swing.*;
public class TestImageLoad extends JPanel {
JLabel face;
BufferedImage bildchen;
TestImageLoad() {
initUI();
}
private void initUI() {
JFrame zeichnen = new JFrame();
face = new JLabel("face");
face.setForeground(Color.RED);
zeichnen.add(this);
this.add(face);
try {
java.net.URL url = this.getClass().getResource("/Informatik/a.jpg");
System.out.println(url);
bildchen = ImageIO.read(url);
} catch (IOException e) {
e.printStackTrace();
}
zeichnen.setDefaultCloseOperation(JFrame.DISPOSE_ON_CLOSE);
zeichnen.pack();
zeichnen.setLocationRelativeTo(null);
zeichnen.setVisible(true);
}
@Override
public void paintComponent(Graphics maler) {
super.paintComponent(maler);
maler.drawImage(bildchen, 0, 0, this);
}
public static void main(String args[]) {
Runnable r = () -> {
new TestImageLoad();
};
SwingUtilities.invokeLater(r);
}
}
Upvotes: 0
Views: 447
Reputation: 168825
Comments on new (working) code
Class loaders are handy things for loading application resources, but there are a few tricks to getting them to work correctly:
(But first a personal aside) I avoid the getResourceAsStream
method for a variety of reasons. Primary among them are:
getResource
returns an URL
. It's easier to debug when this goes wrong, as we can print out the URL and check it points where we think it should. If the resource was not found, it will be null
, but it can also warn when we have two identically named resources in the class-path and it is not fetching the one we want. The latter is pretty rare, but it does happen.InputStream
from the URL, it is but a single method call. Easy. OTOH many methods work better (or even only) with a URL
or (a String
that represents) a File
etc. or a BufferedInputStream
(that applies to Java Sound particularly, possible other things) and the stream returned from the getResourceAsStream
method is typically not buffered. Again, it is but a single line of code to fix that, but it can trip us up. And if the method takes an URL, that is all handled automatically./
to ensure they are going directly from the root of the class-path. That works fine with getResource
, but .. differently with getResourceAsStream
. To be honest, people have tried to explain the intricacies of the differences, but I tune out. getResource
works and that's what I use.OK, now I've explained why I advised to use getResource
, back into the tricks.
/
as a prefix.main
method (setting an icon for the host JFrame
). It turned out that the call was using the system or root class loader which is only intended to load system (e.g. the JRE API classes) resources. It has a very limited class path in order to make searches faster. The context class loader is the one on which our embedded resources can be fetched. But a call like TimeZone.class.getClassLoader()
will generally fetch the root class loader, not the context. To get the context class loader, it always works of the code is called from the constructor or a method of our custom class, and using an instantiated object (like this
).Old Code
The original code added the label to the frame, but not the custom painted panel that drew the image! The way to do it would be to add the custom painted panel to the frame, then add the label to that panel.
The original code was a mess. I reorganized it and fixed many things while doing so. So many things I've forgotten what. Instead refer to this code for details.
First check this code works on your system / environment, then get this code to work with your image, which still seems to be a problem.
import java.awt.Color;
import java.awt.Graphics;
import java.awt.image.BufferedImage;
import java.io.IOException;
import java.net.URL;
import javax.imageio.ImageIO;
import javax.swing.*;
public class TestImageLoad extends JPanel {
JLabel face;
BufferedImage bildchen;
TestImageLoad() {
initUI();
}
private void initUI() {
JFrame zeichnen = new JFrame();
face = new JLabel("face");
face.setForeground(Color.RED);
zeichnen.add(this);
this.add(face);
try {
URL url = new URL("https://i.sstatic.net/OVOg3.jpg");
bildchen = ImageIO.read(url);
} catch (IOException e) {
e.printStackTrace();
}
zeichnen.setDefaultCloseOperation(JFrame.DISPOSE_ON_CLOSE);
zeichnen.pack();
zeichnen.setLocationRelativeTo(null);
zeichnen.setVisible(true);
}
@Override
public void paintComponent(Graphics maler) {
super.paintComponent(maler);
maler.drawImage(bildchen, 0, 0, this);
}
public static void main(String args[]) {
Runnable r = () -> {
new TestImageLoad();
};
SwingUtilities.invokeLater(r);
}
}
Upvotes: 1