2

In Eclipse, when I run the code, this works:

import javax.swing.ImageIcon;
import javax.swing.JFrame;
import javax.swing.JMenu;
import javax.swing.JMenuBar;
import javax.swing.JMenuItem;


   public class Main {

    public static void main(String[] args) {

        JFrame frame = new JFrame("test viewing images");

        frame.setSize(600,300);     
        frame.setLocationRelativeTo(null); // centered on monitor   
        frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);


        /**
         * Menu Bar stuff
         */

        JMenuBar menuBar;
        JMenu menu;
        JMenuItem menuItem;

        // MENU BAR 
        menuBar = new JMenuBar();
        frame.setJMenuBar(menuBar);
        menuBar.setVisible(true);

        // MENU 1
        menu = new JMenu("File");
        menuBar.add(menu);

            // MENU 1 ITEM
            ImageIcon icon = new ImageIcon("src/Action-exit-icon.png");     
            menuItem = new JMenuItem("Exit Program", icon);
            menu.add(menuItem);


        frame.setVisible(true);

    }

   }

And here's the File Structure from my Package Explorer:

ShowImage (project)
 > src / Main.java
 > src / Action-exit-icon.png

Also, this workspace is located in Z:\eclipse_projects

I can see the ImageIcon icon = new ImageIcon("src/Action-exit-icon.png"); is working nicely, and the menuBar does it's job.

Now let's Export this project, and I'll email the JAR to a friend of mine.

  1. Right-click project > Select Export
  2. Select Java > Runnable JAR File
  3. I choose the Main File in Launch configuration
  4. Export destination: my desktop
  5. Library handling: Extract required libraries into generated JAR
  6. go to my desktop, double-click the ShowImage.jar

The JFrame shows up, but the Action-exit-icon.png isn't appearing at all.

When I open the ShowImage.jar, to view it's contents, I see the Main.class, Action-exit-icon.png, META-INF.

Ok, I'm seriously confused about how to reference an image, or any resource now. What am I doing wrong?

Andrew Thompson
  • 168,117
  • 40
  • 217
  • 433
coffeemonitor
  • 12,780
  • 34
  • 99
  • 149

1 Answers1

7
new ImageIcon("src/Action-exit-icon.png"); 

The String constructor for an ImageIcon presumes the string to represent a File path.

This image is obviously an application resource, and will become an embedded resource by the time of deployment (in a Jar). Therefore it must be accessed by URL from the run-time class-path of the app., like so:

new ImageIcon(getClass().getResource("/src/Action-exit-icon.png")); 

Overhauling the code, I get this:

import java.awt.Color;
import javax.swing.*;

public class JavaGui148 {

    public JComponent getGUI() {
        JPanel p = new JPanel();

        p.setBackground(Color.GREEN);

        return p;
    }

    public JMenuBar getMenuBar() {
        /**
         * Menu Bar stuff
         */
        JMenuBar menuBar;
        JMenu menu;
        JMenuItem menuItem;

        // MENU BAR 
        menuBar = new JMenuBar();
        menuBar.setVisible(true);

        // MENU 1
        menu = new JMenu("File");
        menuBar.add(menu);

        // MENU 1 ITEM
        ImageIcon icon = new ImageIcon(getClass().getResource(
                "/src/Action-exit-icon.png"));
        menuItem = new JMenuItem("Exit Program", icon);
        menu.add(menuItem);

        return menuBar;
    }

    public static void main(String[] args) {
        Runnable r = new Runnable() {

            @Override
            public void run() {
                JavaGui148 gui = new JavaGui148();

                JFrame f = new JFrame("Demo");
                f.setJMenuBar(gui.getMenuBar());
                f.add(gui.getGUI());
                // Ensures JVM closes after frame(s) closed and
                // all non-daemon threads are finished
                f.setDefaultCloseOperation(JFrame.DISPOSE_ON_CLOSE);
                // See http://stackoverflow.com/a/7143398/418556 for demo.
                f.setLocationByPlatform(true);

                // ensures the frame is the minimum size it needs to be
                // in order display the components within it
                f.pack();
                // should be done last, to avoid flickering, moving,
                // resizing artifacts.
                f.setVisible(true);
            }
        };
        // Swing GUIs should be created and updated on the EDT
        // http://docs.oracle.com/javase/tutorial/uiswing/concurrency/initial.html
        SwingUtilities.invokeLater(r);
    }
}
Andrew Thompson
  • 168,117
  • 40
  • 217
  • 433
  • I've actually tried this, and in Eclipse the getClass() is underlined in red. If I attempt to launch it, I get this: "Cannot make a static reference to the non-static method getClass() from the type Object. – coffeemonitor Dec 19 '12 at 01:03
  • i think because of the fact that your calling it in static/main method – Festus Tamakloe Dec 19 '12 at 01:07
  • It requires the class of an object created by the context class loader. None of the J2SE classes are loaded using that loader, but something like `Main userObj = new Main(); ..userObj.getClass().getResource("/src/Action-exit-icon.png"));` might do the trick. – Andrew Thompson Dec 19 '12 at 01:07
  • I'm still very new to Java, are you saying I should type Main userObj = new Main(); somewhere inside my main function? – coffeemonitor Dec 19 '12 at 01:10
  • @coffeemonitor Try using `this`. `this.getClass().getResource("/src/Action-exit-icon.png")` – Smit Dec 19 '12 at 01:15
  • 1
    @smit There is no `this` in `main`. -- Yes I was suggesting something like `Main userObj = new Main();` in the `main`, but that in itself is simply a kludge. .. – Andrew Thompson Dec 19 '12 at 01:16
  • 2
    Well, your idea worked. I did place Main userObj = new Main(); inside my main function. I had to type userObj.getClass().getResource("Action-exit-icon.png") to get it to work. Kludge? are you saying it's not ideal to do it this way? – coffeemonitor Dec 19 '12 at 01:19
  • @AndrewThompson +1 Thank you for correcting me. Didn't realized he is working in `main`. – Smit Dec 19 '12 at 01:19
  • *"Kludge? are you saying it's not ideal to do it this way?"* Sorry, missed that comment. It is primarily that creating an object just to get an icon has a bad smell to me. But then, as you can see from my code, it creates a `JavaGui148` object to get access to the (non static) methods that provide the menu bar and GUI. Glad you got it sorted using `getResource()`. – Andrew Thompson Dec 19 '12 at 01:43
  • @coffeemonitor Sounds like you need to use `ClassName.class.getResource(...)` – Troyseph Oct 22 '14 at 07:18
  • @SebastianTroy No, that has too high a risk of getting the wrong class loader. I meant what I wrote. – Andrew Thompson Oct 22 '14 at 07:20
  • @AndrewThompson I was replying to OP, he was trying to get a class loader in a static method and they way to do that is as I mentioned. For something as simple as his implementation, I highly doubt he'd ever run into issues like getting the wrong class laoder. – Troyseph Oct 22 '14 at 08:36