如何将图像添加到JPanel?

时间:2008-11-18 17:36:05

标签: java image swing jpanel

我有一个JPanel,我想添加动态生成的JPEG和PNG图像。

到目前为止我在Swing Tutorials中看到的所有示例,特别是在Swing examples使用ImageIcon s。

我将这些图像生成为字节数组,并且它们通常比它们在示例中使用的常用图标大,为640x480。

  1. 使用ImageIcon类在JPanel中显示大小的图像是否存在(性能或其他)问题?
  2. 通常的做法是什么?
  3. 如何在不使用ImageIcon类的情况下将图像添加到JPanel?
  4. 编辑:仔细检查教程和API会显示您无法将ImageIcon直接添加到JPanel。相反,它们通过将图像设置为JLabel的图标来实现相同的效果。这感觉不对......

14 个答案:

答案 0 :(得分:507)

如果您使用的是JPanel,那么可能正在使用Swing。试试这个:

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

图像现在是摆动组件。它会像任何其他组件一样受制于布局条件。

答案 1 :(得分:242)

以下是我的工作方式(有关如何加载图片的更多信息):

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
    protected void paintComponent(Graphics g) {
        super.paintComponent(g);
        g.drawImage(image, 0, 0, this); // see javadoc for more info on the parameters            
    }

}

答案 2 :(得分:33)

弗雷德哈斯拉姆的方式很好。我在文件路径上遇到了麻烦,因为我想在我的jar中引用一个图像。为此,我使用了:

BufferedImage wPic = ImageIO.read(this.getClass().getResource("snow.png"));
JLabel wIcon = new JLabel(new ImageIcon(wPic));

由于我只需要使用此方法加载有限数量(约10个)图像,因此效果非常好。它获取文件而不必拥有正确的相对文件路径。

答案 3 :(得分:32)

我认为不需要任何子类。只需使用Jlabel。您可以将图像设置为Jlabel。因此,调整Jlabel的大小,然后用图像填充它。没关系。这就是我的方式。

答案 4 :(得分:20)

您可以通过使用免费SwingX库中的JXImagePanel类来完全避免滚动自己的Component子类。

Download

答案 5 :(得分:11)

JLabel imgLabel = new JLabel(new ImageIcon("path_to_image.png"));

答案 6 :(得分:8)

  1. 不应该有任何问题(除非您对非常大的图像有任何一般性问题)。
  2. 如果您正在谈论将多个图像添加到单个面板,我会使用ImageIcon s。对于单个图像,我会考虑创建JPanel的自定义子类并覆盖其paintComponent方法来绘制图像。
  3. (见2)

答案 7 :(得分:7)

你可以创建JPanel的子类 - 这是我的ImagePanel的一个摘录,它将图像放在5个位置中的任何一个,上/下,上/右,中/中,下/左或下/右:

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); }
    }

答案 8 :(得分:6)

JPanel几乎总是子类的错误类。为什么不将JComponent作为子类?

ImageIcon存在一个小问题,即构造函数阻止读取图像。从应用程序jar加载时不是真正的问题,但也许你可能通过网络连接阅读。即使在JDK演示中,也有很多AWT时代使用MediaTrackerImageObserver和朋友的例子。

答案 9 :(得分:6)

我正在做一个与我正在进行的私人项目非常相似的事情。到目前为止,我已经生成了高达1024x1024的图像,没有任何问题(内存除外),并且可以非常快速地显示它们而没有任何性能问题。

覆盖JPanel子类的paint方法是过度的,需要比你需要做的更多的工作。

我这样做的方式是:

Class MapIcon implements Icon {...}

OR

Class MapIcon extends ImageIcon {...}

用于生成图像的代码将在此类中。我使用BufferedImage绘制到当时调用paintIcon()时,使用g.drawImvge(bufferedImage);这样可以减少生成图像时完成的闪烁次数,并且可以对其进行处理。

接下来我扩展JLabel:

Class MapLabel extends Scrollable, MouseMotionListener {...}

这是因为我想把我的图像放在滚动窗格上,即。显示图像的一部分并让用户根据需要滚动。

然后我使用JScrollPane来保存MapLabel,它只包含MapIcon。

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

scrollPane.getViewport ().add (mapLabel);

但是对于你的场景(每次只显示整个图像)。您需要将MapLabel添加到顶部JPanel,并确保将它们全部调整为图像的完整大小(通过重写GetPreferredSize())。

答案 10 :(得分:5)

在项目目录中创建一个源文件夹,在本例中我称之为Images。

import socket
socket.gethostname()

答案 11 :(得分:4)

这个答案是@ shawalli回答的补充......

我想在我的jar中引用一个图像,但是我没有使用BufferedImage,而是简单地做了这个:

 JPanel jPanel = new JPanel();      
 jPanel.add(new JLabel(new ImageIcon(getClass().getClassLoader().getResource("resource/images/polygon.jpg"))));

答案 12 :(得分:3)

您可以避免使用自己的Component和SwingX库以及ImageIO类:

File f = new File("hello.jpg");
JLabel imgLabel = new JLabel(new ImageIcon(file.getName()));

答案 13 :(得分:0)

我可以看到许多答案,而不是真正解决OP的三个问题。

1)关于性能的一个词:字节数组可能效率不高,除非您可以使用与显示适配器当前分辨率和颜色深度匹配的精确像素字节顺序。

要获得最佳绘图性能,只需将图像转换为BufferedImage,即使用与当前图形配置对应的类型生成的BufferedImage。请参阅https://docs.oracle.com/javase/tutorial/2d/images/drawonimage.html

上的createCompatibleImage

这些图像在没有任何编程工作的情况下绘制几次后将自动缓存在显示卡内存中(这是Java 6中Swing的标准配置),因此实际绘图将花费的时间可以忽略不计 - 如果您没有更改图片。

更改图像将在主内存和GPU内存之间进行额外的内存传输 - 这很慢。避免将图像“重绘”为BufferedImage,因此请尽量避免使用getPixel和setPixel。

例如,如果您正在开发游戏,而不是将所有游戏角色绘制到BufferedImage然后再绘制到JPanel,那么将所有角色加载为较小的BufferedImages会快得多,并在您的游戏中逐个绘制它们JPanel代码处于正确的位置 - 这样,除了用于缓存的图像的初始传输之外,主存储器和GPU存储器之间没有额外的数据传输。

ImageIcon将在底层使用BufferedImage - 但基本上使用正确的图形模式分配BufferedImage是关键,并且没有任何努力做到这一点。

2)通常的做法是在JPanel的重写paintComponent方法中绘制BufferedImage。尽管Java支持大量额外的好处,例如控制缓存在GPU内存中的VolatileImages的缓冲链,但是没有必要使用任何这些,因为Java 6在不暴露所有这些GPU加速细节的情况下做得相当不错。 / p>

请注意,GPU加速可能不适用于某些操作,例如拉伸半透明图像。

3)不要添加。只需按上述方式绘制它:

@Override
protected void paintComponent(Graphics g) {
    super.paintComponent(g);
    g.drawImage(image, 0, 0, this); 
}

如果图像是布局的一部分,“添加”是有意义的。如果您需要将此作为填充JPanel的背景或前景图像,只需在paintComponent中绘制。如果您更喜欢酿造可以显示图像的通用Swing组件,那么它是相同的故事(您可以使用JComponent并覆盖其paintComponent方法) - 然后将 this 添加到GUI组件的布局中

4)如何将数组转换为Bufferedimage

将字节数组转换为PNG,然后加载它是非常耗费资源的。更好的方法是将现有的字节数组转换为BufferedImage。

为此:不要使用for循环并复制像素。那非常慢。代替:

  • 学习BufferedImage的首选字节结构(现在可以安全地假设RGB或RGBA,每像素4个字节)
  • 学习扫描线和扫描使用(例如,您可能有142像素宽的图像 - 但在现实生活中将存储为256像素宽的字节数组,因为处理它并加快未使用的像素屏蔽未使用的像素GPU硬件)
  • 然后根据这些原则建立数组后,BufferedImage的setRGB数组方法可以将数组复制到BufferedImage。