357

我有一个JPanel,我想在其中添加即时生成的 JPEG 和 PNG 图像。

到目前为止,我在Swing 教程中看到的所有示例,特别是在Swing 示例中都使用ImageIcons。

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

  1. 使用 ImageIcon 类在 JPanel 中显示该大小的图像是否有任何(性能或其他)问题?
  2. 通常的做法是什么?
  3. 如何在不使用 ImageIcon 类的情况下将图像添加到 JPanel?

编辑:对教程和 API 的更仔细检查表明您不能将 ImageIcon 直接添加到 JPanel。相反,它们通过将图像设置为 JLabel 的图标来实现相同的效果。只是感觉不太对...

4

14 回答 14

539

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

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

该图像现在是一个摆动组件。它会像任何其他组件一样受到布局条件的影响。

于 2010-04-25T00:46:40.017 回答
259

这是我的做法(有关如何加载图像的更多信息):

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            
    }

}
于 2008-11-18T17:50:48.210 回答
41

Fred Haslam 的方法很好用。我在文件路径上遇到了麻烦,因为我想在我的 jar 中引用一个图像。为此,我使用了:

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

由于我只有有限数量(大约 10 个)需要使用此方法加载的图像,因此效果很好。它无需正确的相对文件路径即可获取文件。

于 2010-12-27T06:49:54.080 回答
33

我认为没有必要对任何东西进行子类化。只需使用 Jlabel。您可以将图像设置为 Jlabel。因此,调整 Jlabel 的大小,然后用图像填充它。没关系。我就是这样做的。

于 2009-10-03T19:07:59.950 回答
19

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

下载

于 2008-11-19T00:37:00.603 回答
11
JLabel imgLabel = new JLabel(new ImageIcon("path_to_image.png"));
于 2013-05-04T21:40:06.673 回答
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); }
    }
于 2008-11-18T17:48:00.947 回答
7
  1. 不应该有任何问题(除了您在使用非常大的图像时可能遇到的任何一般问题)。
  2. 如果您正在谈论将多个图像添加到单个面板,我会使用ImageIcons. 对于单个图像,我会考虑制作一个自定义子类JPanel并覆盖其paintComponent绘制图像的方法。
  3. (见2)
于 2008-11-18T17:49:03.563 回答
6

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

ImageIcon构造函数阻止读取图像有一个小问题。从应用程序 jar 加载时并不是真正的问题,但如果您可能正在通过网络连接进行读取,则可能会出现问题。即使在 JDK 演示中,也有很多 AWT 时代的使用示例和MediaTracker朋友。ImageObserver

于 2008-11-18T18:21:05.323 回答
6

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

覆盖 JPanel 子类的paint 方法是多余的,并且需要比您需要做的更多的工作。

我这样做的方式是:

Class MapIcon implements Icon {...}

或者

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())。

于 2008-11-18T18:28:11.940 回答
5

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

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

 JPanel jPanel = new JPanel();      
 jPanel.add(new JLabel(new ImageIcon(getClass().getClassLoader().getResource("resource/images/polygon.jpg"))));
于 2015-09-30T17:10:45.693 回答
5

在您的项目目录中创建一个源文件夹,在本例中,我将其命名为 Images。

JFrame snakeFrame = new JFrame();
snakeFrame.setBounds(100, 200, 800, 800);
snakeFrame.setVisible(true);
snakeFrame.add(new JLabel(new ImageIcon("Images/Snake.png")));
snakeFrame.pack();
于 2016-04-18T05:02:53.293 回答
5

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

File f = new File("hello.jpg");
JLabel imgLabel = new JLabel(new ImageIcon(file.getName()));
于 2018-02-23T07:32:44.013 回答
1

我可以看到很多答案,并没有真正解决 OP 的三个问题。

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

To achieve the best drawing performance, simply convert your image to a BufferedImage which is generated with a type corresponding to your current graphics configuration. See createCompatibleImage at https://docs.oracle.com/javase/tutorial/2d/images/drawonimage.html

These images will be automatically cached on the display card memory after drawing a few times without any programming effort (this is standard in Swing since Java 6), and therefore the actual drawing will take negligible amount of time - if you did not change the image.

Altering the image will come with an additional memory transfer between main memory and GPU memory - which is slow. Avoid "redrawing" the image into a BufferedImage therefore, avoid doing getPixel and setPixel at all means.

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

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

2)执行此操作的常用方法是在 JPanel 的重写 paintComponent 方法中绘制 BufferedImage。尽管 Java 支持大量额外的好东西,例如控制缓存在 GPU 内存中的 VolatileImages 的缓冲区链,但由于 Java 6 在不暴露 GPU 加速的所有这些细节的情况下做得相当好,因此不需要使用任何这些东西。

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

3)不要添加。就像上面提到的那样画它:

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

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

4)如何将数组转换为 Bufferedimage

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

为此:不要使用 for 循环和复制像素。这是非常非常缓慢的。反而:

  • 学习 BufferedImage 的首选字节结构(现在可以安全地假设 RGB 或 RGBA,即每个像素 4 个字节)
  • 学习使用中的扫描线和扫描大小(例如,您可能有一个 142 像素宽的图像 - 但在现实生活中,它将存储为 256 像素宽的字节数组,因为它可以更快地处理它并通过 GPU 硬件屏蔽未使用的像素)
  • 那么一旦你根据这些原则构建了一个数组,BufferedImage 的 setRGB 数组方法就可以将你的数组复制到 BufferedImage 中。
于 2018-03-10T00:36:41.833 回答