null
or "pixel perfect" layouts are an illusion. There are so many factors which go into determining the best size/position and relationship of components, you'd spend a live time trying to make it work properly ever time on every platform.
Instead, make use of all the work which has already been done, see Laying Out Components Within a Container for more details.
ImageIcon(String)
is expecting to find a file on the disk at the location you've specified. Since you're using a relative path, this will be offset from the "working" location of the app (that is, the directory is was launched from).
In cases where the image "doesn't appear to be loading", I'd consider using something like System.out.println(new File("kenya.png").exists());
, this will give you a good indication whether the issue is with the image file itself or the image file can't be found within the current context.
If you want to know what the current working directory is, you can use something like System.out.println(System.getProperty("user.dir"));
ImageIcon
will also attempt to load the image in a background thread, meaning that if it fails, it will do so silently. For this (and a few other) reasons, I prefer to use ImageIO.read
, see Reading/Loading an Image
Now, one way to overcome this issue with external files, is to "embed" them within the application context directly. Generally this is done by having them included in the resulting Jar. Depending you build system and IDE, you should be able to add images to a "location" within the project, which will make them easily available during development and automatically include them in the Jar when it's exported.
In Netbeans (using an Ant based project), you can simply drop the resources into the src
directory.
Once they are "embedded", you can no longer treat them as files, instead, you need to make use of Class#getResource
or Class#getResourceAsStream
depending on your needs
For example...

BYO own images (I shouldn't have to say this, but you'd be surprised).
I've also deliberately set the number of columns to 3 so that the "highlighting" shows up better.
import java.awt.Color;
import java.awt.EventQueue;
import java.awt.GridBagLayout;
import java.awt.GridLayout;
import java.awt.Image;
import java.awt.image.BufferedImage;
import java.io.IOException;
import java.util.logging.Level;
import java.util.logging.Logger;
import javax.imageio.ImageIO;
import javax.swing.ImageIcon;
import javax.swing.JFrame;
import javax.swing.JLabel;
import javax.swing.JPanel;
public class Test {
public static void main(String[] args) {
new Test();
}
public Test() {
EventQueue.invokeLater(new Runnable() {
@Override
public void run() {
try {
JFrame frame = new JFrame();
frame.add(new TestPane());
frame.pack();
frame.setLocationRelativeTo(null);
frame.setVisible(true);
} catch (IOException ex) {
Logger.getLogger(Test.class.getName()).log(Level.SEVERE, null, ex);
}
}
});
}
public class TestPane extends JPanel {
public TestPane() throws IOException {
setLayout(new GridLayout(-1, 3));
for (int index = 0; index < 8; index++) {
BufferedImage img = ImageIO.read(getClass().getResource("/images/Cute" + (index + 1) + ".png"));
JLabel label = new JLabel(new ImageIcon(img.getScaledInstance(-1, 50, Image.SCALE_SMOOTH)));
JPanel panel = new JPanel(new GridBagLayout());
panel.add(label);
if (index % 2 == 0) {
panel.setBackground(Color.DARK_GRAY);
}
add(panel);
}
}
}
}
Now, if you want the images to be selectable, then you should consider using a JList
instead, this can support columns and rows (it defaults to a single column)
If you prefer to have the images listed in a single column (for the above example), then change the number of columns the GridLayout
is using, much simpler then trying to manually recalculate the positions yourself
Image#setScaledInstance
is neither efficient or pretty, see
for more details