我有一个JPanel,我想添加我在飞行中生成的JPEG和PNG图像。
到目前为止,我在Swing教程中看到的所有例子,特别是在摇摆不定的例子中,都使用了__abc0。
我将这些图像生成为字节数组,它们通常比示例中使用的普通图标大,为640x480。
编辑:更仔细的检查教程和API显示,你不能直接添加一个ImageIcon到JPanel。相反,它们通过将图像设置为JLabel的图标来实现相同的效果。这感觉不对……
你可以子类化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); } }
ImageIcon
JPanel
paintComponent
下面是我怎么做的(有更多关于如何加载图像的信息):
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 } }
JPanel几乎总是要子类化的错误类。为什么不子类化JComponent呢?
JComponent
ImageIcon有一个小问题,构造函数阻塞读取图像。当从应用程序jar中加载时,这并不是真正的问题,但如果您可能通过网络连接进行读取,则可能存在问题。在awt时代有很多使用MediaTracker、ImageObserver等的例子,甚至在JDK演示中也是如此。
MediaTracker
ImageObserver
我在自己的一个私人项目中也在做类似的事情。到目前为止,我已经生成了高达1024x1024的图像,没有任何问题(除了内存),并且可以非常快速地显示它们,没有任何性能问题。
重写JPanel子类的paint方法是多余的,并且需要比您需要做的更多的工作。
我的做法是:
Class MapIcon implements Icon {...}
或
Class MapIcon extends ImageIcon {...}
用于生成图像的代码将在该类中。我使用BufferedImage来绘制然后当paintIcon()被调用时,使用g.drawImvge(BufferedImage);这样可以减少生成图像时的闪烁次数,并且可以使用线程。
接下来我扩展JLabel:
Class MapLabel extends Scrollable, MouseMotionListener {...}
这是因为我想把我的图像放在滚动窗格上,即显示图像的一部分,并让用户根据需要滚动。
因此,我使用JScrollPane来保存只包含MapIcon的MapLabel。
MapIcon map = new MapIcon (); MapLabel mapLabel = new MapLabel (map); JScrollPane scrollPane = new JScrollPane(); scrollPane.getViewport ().add (mapLabel);
但对于你的场景(每次只显示整个图像)。您需要将MapLabel添加到顶部JPanel,并确保将它们全部设置为图像的完整大小(通过覆盖GetPreferredSize())。
你可以通过使用免费SwingX库中的JXImagePanel类来避免完全滚动你自己的Component子类。
下载
我认为没有必要为任何东西创建子类。只需使用Jlabel。您可以将图像设置为Jlabel。因此,调整Jlabel的大小,然后用图像填充它。它的好。我就是这么做的。
如果您正在使用jpanel,那么您可能正在使用Swing。试试这个:
BufferedImage myPicture = ImageIO.read(new File("path-to-file")); JLabel picLabel = new JLabel(new ImageIcon(myPicture)); add(picLabel);
图像现在是一个摆动组件。它像其他组件一样受到布局条件的制约。
弗雷德·哈斯拉姆的方法很有效。但是我在文件路径上遇到了麻烦,因为我想引用jar中的图像。为了做到这一点,我使用:
BufferedImage wPic = ImageIO.read(this.getClass().getResource("snow.png")); JLabel wIcon = new JLabel(new ImageIcon(wPic));
因为我只有有限的数量(大约10)的图像,我需要使用这种方法加载,它工作得很好。它不需要有正确的相对文件路径就可以获取文件。
JLabel imgLabel = new JLabel(new ImageIcon("path_to_image.png"));
这个答案是对@shawalli的回答的补充…
我想在我的罐子里引用一个图像,但不是有一个BufferedImage,我简单地这样做:
JPanel jPanel = new JPanel(); jPanel.add(new JLabel(new ImageIcon(getClass().getClassLoader().getResource("resource/images/polygon.jpg"))));
在项目目录中创建一个源文件夹,在本例中我称之为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();
你可以避免使用自己的Components和SwingX库和ImageIO类:
Component
ImageIO
File f = new File("hello.jpg"); JLabel imgLabel = new JLabel(new ImageIcon(file.getName()));
我可以看到很多答案,但并没有真正解决OP的三个问题。
1)关于性能的一个词:字节数组可能是低效的,除非你可以使用一个精确的像素字节排序,匹配你的显示适配器当前的分辨率和颜色深度。
要获得最佳的绘图性能,只需将图像转换为BufferedImage,该BufferedImage生成的类型与当前图形配置相对应。参见https://docs.oracle.com/javase/tutorial/2d/images/drawonimage.html的createCompatibleImage
这些图像将在绘制几次后自动缓存到显卡内存中,而不需要任何编程工作(这是自Java 6以来Swing的标准),因此实际绘制将花费可以忽略不计的时间- 如果您没有更改图像。
改变图像将带来主存和GPU内存之间的额外内存传输,这是缓慢的。避免将图像“重绘”到BufferedImage中,因此,避免使用getPixel和setPixel。
例如,如果你正在开发一个游戏,而不是将所有的游戏角色绘制到一个BufferedImage,然后再绘制到一个JPanel,它会更快地将所有的角色加载为更小的BufferedImage,并在你的JPanel代码中一个一个地在它们适当的位置绘制它们——这样在主存和GPU内存之间就没有额外的数据传输,除了用于缓存的图像的初始传输。
ImageIcon将在底层使用BufferedImage -但基本上分配一个BufferedImage与适当的图形模式是关键,没有努力做到这一点。
通常的方法是在JPanel的一个重写的paintComponent方法中绘制一个BufferedImage。虽然Java支持大量额外的功能,比如控制GPU内存中缓存的volatile image的缓冲链,但是没有必要使用这些功能,因为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组件布局中。
如何将数组转换为buffereimage
将字节数组转换为PNG,然后加载它相当耗费资源。更好的方法是将现有的字节数组转换为BufferedImage。
为此:不使用for循环和复制像素。这是非常非常慢的。而不是: