views:

60652

answers:

9

I have a JPanel to which I'd like to add JPEG and PNG images that I generate on the fly.

All the examples I've seen so far in the Swing Tutorials, specially in the Swing examples use ImageIcons.

I'm generating these images as byte arrays, and they are usually larger than the common icon they use in the examples, at 640x480.

  1. Is there any (performance or other) problem in using the ImageIcon class to display an image that size in a JPanel ?
  2. What's the usual way of doing it ?
  3. How to add an image to a JPanel without using the ImageIcon class ?

Edit: A more careful examination of the tutorials and the API shows that you cannot add an ImageIcon directly to a JPanel. Instead, they achieve the same effect by setting the image as an icon of a JLabel. This just doesn't fill right...

+1  A: 

You can subclass JPanel - here is an extract from my ImagePanel, which puts an image in any one of 5 locations, top/left, top/right, middle/middle, bottom/left or bottom/right:

protected void paintComponent(Graphics gc) {
    super.paintComponent(gc);

    Dimension                           cs=getSize();                           // component size

    gc=gc.create();
    gc.clipRect(insets.left,insets.top,(cs.width-insets.left-insets.right),(cs.height-insets.top-insets.bottom));
    if(mmImage!=null) { gc.drawImage(mmImage,(((cs.width-mmSize.width)/2)       +mmHrzShift),(((cs.height-mmSize.height)/2)        +mmVrtShift),null); }
    if(tlImage!=null) { gc.drawImage(tlImage,(insets.left                       +tlHrzShift),(insets.top                           +tlVrtShift),null); }
    if(trImage!=null) { gc.drawImage(trImage,(cs.width-insets.right-trSize.width+trHrzShift),(insets.top                           +trVrtShift),null); }
    if(blImage!=null) { gc.drawImage(blImage,(insets.left                       +blHrzShift),(cs.height-insets.bottom-blSize.height+blVrtShift),null); }
    if(brImage!=null) { gc.drawImage(brImage,(cs.width-insets.right-brSize.width+brHrzShift),(cs.height-insets.bottom-brSize.height+brVrtShift),null); }
    }
Software Monkey
A: 
  1. There shouldn't be any problem (other than any general problems you might have with very large images).
  2. If you're talking about adding multiple images to a single panel, I would use ImageIcons. For a single image, I would think about making a custom subclass of JPanel and overriding its paintComponent method to draw the image.
  3. (see 2)
Michael Myers
+12  A: 

Here's how I do it (with a little more info on how to load an image):

import java.awt.Graphics;
import java.awt.image.BufferedImage;
import java.io.File;
import java.io.IOException;
import java.util.logging.Level;
import java.util.logging.Logger;
import javax.imageio.ImageIO;
import javax.swing.JPanel;

public class ImagePanel extends JPanel{

    private BufferedImage image;

    public ImagePanel() {
       try {                
          image = ImageIO.read(new File("image name and path"));
       } catch (IOException ex) {
            // handle exception...
       }
    }

    @Override
    public void paintComponent(Graphics g) {
        g.drawImage(image, 0, 0, null); // see javadoc for more info on the parameters

    }

}
bcash
You're reading in the image from a file every time you draw the screen? That's crazy!
Paul Tomblin
I was going to say that, but I think this is just a little demonstration (not actual production code).
Michael Myers
I'm not going to vote it down or anything, but I think this example would be really improved if he moved that BufferedImage creation into a separate method. And showed that after creating/changing the image, you should call repaint().
Paul Tomblin
Opps... put the example together a little to quick...
bcash
IOException is thrown by ImageIO.read, not drawImage
Dutow
@Dutow - Good call, I edited the answer. Thanks.
bcash
A: 

JPanel is almost always the wrong class to subclass. Why wouldn't you subclass JComponent?

There is a slight problem with ImageIcon in that the constructor blocks reading the image. Not really a problem when loading from the application jar, but maybe if you're potentially reading over a network connection. There's plenty of AWT-era examples of using MediaTracker, ImageObserver and friends, even in the JDK demos.

Tom Hawtin - tackline
+1  A: 

I'm doing something very similar in a private project I'm working on. Thus far I've generated images up to 1024x1024 without any problems (except memory) and can display them very quickly and without any performance problems.

Overriding the paint method of JPanel subclass is overkill and requires more work than you need to do.

The way I do it is:

Class MapIcon implements Icon {...}

OR

Class MapIcon extends ImageIcon {...}

The code you use to generate the image will be in this class. I use a BufferedImage to draw onto then when the paintIcon() is called, use g.drawImvge(bufferedImage); This reduces the amount of flashing done while you generate your images, and you can thread it.

Next I extend JLabel:

Class MapLabel extends Scrollable, MouseMotionListener {...}

This is because I want to put my image on a scroll pane, I.e. display part of the image and have the user scroll around as needed.

So then I use a JScrollPane to hold the MapLabel, which contains only the MapIcon.

MapIcon map = new MapIcon (); 
MapLabel mapLabel = new MapLabel (map);
JScrollPane scrollPane = new JScrollPane();

scrollPane.getViewport ().add (mapLabel);

But for your scenario (just show the whole image every time). You need to add the MapLabel to the top JPanel, and make sure to size them all to the full size of the image (by overriding the GetPreferredSize()).

Thomas Jones-Low
+10  A: 

You can avoid rolling your own Component subclass completely by using the JXImagePanel class from the free SwingX libraries.

Download

Dan Vinton
+5  A: 

I think there is no need to subclass of anything. Just use a Jlabel. You can set an image into a Jlabel. So, resize the Jlabel then fill it with an image. Its OK. This is the way I do.

CoderTim
Simpler by a long shot.
Silence
+4  A: 

If you are using JPanels, then are probably working with Swing. Try this:

BufferedImage myPicture = ImageIO.read(new File("path-to-file"));
JLabel picLabel = new JLabel(new ImageIcon( myPicture ))
add( picLabel )

The image is now a swing component. It becomes subject to layout conditions like any other component.

Fred Haslam
+1  A: 

public ImagePanel() { try {
image = ImageIO.read(new File("image name and path")); } catch (IOException ex) { // handle exception... } }

Date Sudhir Baban