代码之家  ›  专栏  ›  技术社区  ›  Leonel

如何向jpanel添加图像?

  •  324
  • Leonel  · 技术社区  · 16 年前

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

    到目前为止,我在 Swing Tutorials 特别是在 Swing examples 使用 ImageIcon S.

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

    1. 在使用ImageIcon类以jpanel格式显示该大小的图像时,是否存在任何(性能或其他)问题?
    2. 什么是 通常的 怎么做?
    3. 如何在不使用ImageIcon类的情况下向jpanel添加图像?

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

    14 回复  |  直到 6 年前
        1
  •  240
  •   Andrew Thompson    8 年前

    以下是我的操作方法(关于如何加载图像的详细信息):

    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
  •  502
  •   Deleted Andrey    11 年前

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

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

    图像现在是一个Swing组件。它会像其他组件一样受布局条件的约束。

        3
  •  32
  •   Lutske Arif    11 年前

    弗雷德·哈斯拉姆的方法很有效。不过,我在文件路径上遇到了问题,因为我想引用JAR中的一个图像。为此,我使用了:

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

    因为我只有一个有限的图像(大约10个),我需要用这个方法加载,所以它工作得很好。它获取文件而不必具有正确的相对文件路径。

        4
  •  31
  •   CoderTim    15 年前

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

        5
  •  19
  •   bobndrew Daniel Hiller    12 年前

    通过使用jximagepanel类,可以避免完全滚动自己的组件子类。 SwingX 图书馆。

    Download

        6
  •  10
  •   user1597002    11 年前
    JLabel imgLabel = new JLabel(new ImageIcon("path_to_image.png"));
    
        7
  •  7
  •   Michael Myers KitsuneYMG    16 年前
    1. 不应该有任何问题(除了对于非常大的图像可能存在的任何一般问题)。
    2. 如果你要在一个面板上添加多个图像,我会使用 ImageIcon 对于一个图像,我想制作一个自定义的子类 JPanel 并超越它 paintComponent 方法绘制图像。
    3. (见2)
        8
  •  6
  •   L. Cornelius Dol    16 年前

    您可以对jpanel进行子类划分-这里是“我的图像面板”的一个摘录,它将图像放在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); }
        }
    
        9
  •  5
  •   Tom Hawtin - tackline    16 年前

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

    有点问题 ImageIcon 这样,构造器将阻止读取图像。从应用程序JAR加载时不是真正的问题,但如果您可能正在通过网络连接进行读取,则可能是问题所在。有很多awt时代的例子 MediaTracker , ImageObserver 还有朋友,甚至在JDK演示中。

        10
  •  5
  •   Thomas Jones-Low    16 年前

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

    重写jpanel子类的paint方法是多余的,并且需要比您需要做的更多的工作。

    我的做法是:

    Class MapIcon implements Icon {...}
    

    Class MapIcon extends ImageIcon {...}
    

    用于生成图像的代码将在此类中。当调用painticon()时,我使用bufferedimage进行绘制,使用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())。

        11
  •  4
  •   user4294909    8 年前

    在项目目录中创建一个源文件夹,在本例中,我称之为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();
    
        12
  •  3
  •   Filipe Brito Danilo Lemes    9 年前

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

    我也想引用我的jar中的一个图像,但我不想使用bufferedimage,而是简单地执行以下操作:

     JPanel jPanel = new JPanel();      
     jPanel.add(new JLabel(new ImageIcon(getClass().getClassLoader().getResource("resource/images/polygon.jpg"))));
    
        13
  •  0
  •   Muskovets    6 年前

    你可以避免使用自己的 Component S和Swingx库 ImageIO 班级:

    File f = new File("hello.jpg");
    JLabel imgLabel = new JLabel(new ImageIcon(file.getName()));
    
        14
  •  0
  •   Gee Bee    6 年前

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

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

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

    这些图像将在没有任何编程努力的情况下被多次缓存在显示卡存储器上(这是自Java 6以来的摆动标准),因此实际绘图将花费可忽略的时间量。 如果 您没有更改图像。

    改变图像会在主存储器和GPU存储器之间进行额外的存储器传输,这是很慢的。因此,请避免将图像“重绘”到BufferedImage中,尽可能避免执行GetPixel和SetPixel。

    例如,如果您正在开发一个游戏,而不是将所有的游戏参与者绘制到BufferedImage,然后再绘制到JPanel,那么将所有参与者加载为较小的BufferedImage,并在JPanel代码中的适当位置逐个绘制它们会更快——这样,主内存和GPU内存之间就不会有额外的数据传输。用于缓存的图像的初始传输点。

    ImageIcon将在引擎盖下使用BufferedImage,但基本上使用 适当的 图形模式是关键,不需要努力去做正确的事情。

    2) 通常的方法是在jpanel的重写paintcomponent方法中绘制BufferedImage。虽然Java支持大量额外的好处,例如缓冲链控制在GPU存储器中缓存的挥发性图像,但是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方法),然后添加 到图形用户界面组件的布局。

    4) 如何将数组转换为BufferedImage

    将字节数组转换为PNG,然后加载它需要大量的资源。更好的方法是将现有的字节数组转换为BufferedImage。

    为此: 不用于循环 并复制像素。那是非常慢的。而是:

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