BufferedImage不会显示在JFrame中

huangapple go评论78阅读模式
英文:

BufferedImage does not appear in JFrame

问题

这是我的第二个Java项目,我完全不懂编程,但我已经在这上面工作了相当长的时间。然而,代码可能会很混乱。我试图在一个JFrame中复制一张图像,但我不知道如何解决所述的问题。这可能是文件格式的问题吗?
当我将包文件夹和图像文件插入作为路径时,它能够编译而没有错误消息,尽管它似乎没有绘制任何东西。当我将路径更改为我的桌面(或者实际上是其他任何地方)时,我会得到以下异常:

Exception in thread "main" java.lang.IllegalArgumentException: input == null!
    at javax.imageio.ImageIO.read(Unknown Source)
    at Informatik.Timezones.main(Timezones.java:287)

显然找不到源,这意味着之前它被正确识别过。也许我是在JFrame上绘制而不是在Label或Panel上绘制,这可能是问题的原因。

这是主方法中生成Panel并绘制图像的代码部分:

JFrame zeichnen = new JFrame(); 
face = new JLabel();
face.setVisible(true);
face.setSize(1000, 1000);
face.setLayout(null);
face.setLocation(0, 0);
zeichnen.add(face);

zeichnen.setVisible(true);
zeichnen.setSize(1000, 1000);

zeichnen.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
zeichnen.setLocationRelativeTo(null);
zeichnen.setResizable(false);
zeichnen.setLayout(null);

try {
    bildchen = ImageIO.read(Timezones.class.getClassLoader().getResourceAsStream("Informatik/a.jpg"));
} catch (IOException e) {
    // TODO Auto-generated catch block
    e.printStackTrace();
}

这是我的示例:

package Informatik;
import java.awt.Graphics;
import java.awt.Graphics2D;
import java.awt.image.BufferedImage; 
import java.io.IOException;

import javax.imageio.ImageIO;

import java.awt.RenderingHints;
import javax.swing.*;

public class Test extends JPanel {

static JLabel face;
static BufferedImage bildchen; 

@Override
public void paintComponent(Graphics maler) {
   super.paintComponent(maler);

   Graphics2D maler2 = (Graphics2D) maler;
   maler2.setRenderingHint(RenderingHints.KEY_ANTIALIASING, RenderingHints.VALUE_ANTIALIAS_ON);
   maler.drawImage(bildchen, 1000, 1000, null);
}

public static void main (String args []) {

JFrame zeichnen = new JFrame(); 
face = new JLabel();
face.setVisible(true);
face.setSize(1000, 1000);
face.setLayout(null);
face.setLocation(0, 0);
zeichnen.add(face);

zeichnen.setVisible(true);
zeichnen.setSize(1000, 1000);
try {
    bildchen = ImageIO.read(Timezones.class.getClassLoader().getResourceAsStream("/Informatik/a.jpg"));
} catch (IOException e) {
    //catch block
    e.printStackTrace();
}
zeichnen.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
zeichnen.setLocationRelativeTo(null);
zeichnen.setResizable(false);
zeichnen.setLayout(null);
}
}

这是新的、可工作的版本:

package Informatik;
import java.awt.Color;
import java.awt.Graphics;
import java.awt.image.BufferedImage;
import java.io.IOException;
import javax.imageio.ImageIO;
import javax.swing.*;

public class TestImageLoad extends JPanel {

JLabel face;
BufferedImage bildchen;

TestImageLoad() {
initUI();
}

private void initUI() {
JFrame zeichnen = new JFrame();
face = new JLabel("face");
face.setForeground(Color.RED);
zeichnen.add(this);
this.add(face);

try {
    java.net.URL url = this.getClass().getResource("/Informatik/a.jpg");
    System.out.println(url);
    bildchen = ImageIO.read(url);
} catch (IOException e) {
    e.printStackTrace();
}
zeichnen.setDefaultCloseOperation(JFrame.DISPOSE_ON_CLOSE);
zeichnen.pack();
zeichnen.setLocationRelativeTo(null);
zeichnen.setVisible(true);
}

@Override
public void paintComponent(Graphics maler) {
super.paintComponent(maler);
maler.drawImage(bildchen, 0, 0, this);
}

public static void main(String args[]) {
Runnable r = () -> {
    new TestImageLoad();
};
SwingUtilities.invokeLater(r);
}
}
英文:

This is my second java project, I am totally new to programming but I have been working on this for quite a while now. However the code could be a mess. I am attempting to reproduce an Image in a JFrame, but I have no idea how to resolve the stated problem. Could it be the fault of the file format?
When I insert the package folder and the image file as a path, it compiles without an error message, even though it seemingly doesn't draw anything. After I changed the path to my Desktop (or really anything else) I get the following exception:

Exception in thread "main" java.lang.IllegalArgumentException: input == null!
at javax.imageio.ImageIO.read(Unknown Source)
at Informatik.Timezones.main(Timezones.java:287)

Apparently the source is not found, which means it was correctly recognized before. Perhaps I am drawing on the JFrame instead of a Label or Panel and this causes the problem.

Here is the part of the code in the main method which generates the Panel and draws the image.

    JFrame zeichnen = new JFrame(); 
face = new JLabel();
face.setVisible(true);
face.setSize(1000, 1000);
face.setLayout(null);
face.setLocation(0, 0);
zeichnen.add(face);
zeichnen.setVisible(true);
zeichnen.setSize(1000, 1000);
zeichnen.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
zeichnen.setLocationRelativeTo(null);
zeichnen.setResizable(false);
zeichnen.setLayout(null);
try {
bildchen =                                                                                  ImageIO.read(Timezones.class.getClassLoader().getResourceAsStream("Info      rmatik/a.jpg"));
} catch (IOException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}

This is my reprex:

package Informatik;
import java.awt.Graphics;
import java.awt.Graphics2D;
import java.awt.image.BufferedImage; 
import java.io.IOException;
import javax.imageio.ImageIO;
import java.awt.RenderingHints;
import javax.swing.*;
public class Test extends JPanel{
static JLabel face;
static BufferedImage bildchen; 
@Override
public void paintComponent(Graphics maler) {
super.paintComponent(maler);
Graphics2D maler2 = (Graphics2D) maler;
maler2.setRenderingHint(RenderingHints.KEY_ANTIALIASING, RenderingHints.VALUE_ANTIALIAS_ON);
maler.drawImage(bildchen, 1000, 1000, null);
}
public static void main (String args []) {
JFrame zeichnen = new JFrame(); 
face = new JLabel();
face.setVisible(true);
face.setSize(1000, 1000);
face.setLayout(null);
face.setLocation(0, 0);
zeichnen.add(face);
zeichnen.setVisible(true);
zeichnen.setSize(1000, 1000);
try {
bildchen = ImageIO.read(Timezones.class.getClassLoader().getResourceAsStream("/Informatik/a.jpg"));
} catch (IOException e) {
//catch block
e.printStackTrace();
}
zeichnen.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
zeichnen.setLocationRelativeTo(null);
zeichnen.setResizable(false);
zeichnen.setLayout(null);
}
}

Here's the new, working version:

package Informatik;
import java.awt.Color;
import java.awt.Graphics;
import java.awt.image.BufferedImage;
import java.io.IOException;
import javax.imageio.ImageIO;
import javax.print.DocFlavor.URL;
import javax.swing.*;
public class TestImageLoad extends JPanel {
JLabel face;
BufferedImage bildchen;
TestImageLoad() {
initUI();
}
private void initUI() {
JFrame zeichnen = new JFrame();
face = new JLabel("face");
face.setForeground(Color.RED);
zeichnen.add(this);
this.add(face);
try {
java.net.URL url = this.getClass().getResource("/Informatik/a.jpg");
System.out.println(url);
bildchen =   ImageIO.read(url);
} catch (IOException e) {
e.printStackTrace();
}
zeichnen.setDefaultCloseOperation(JFrame.DISPOSE_ON_CLOSE);
zeichnen.pack();
zeichnen.setLocationRelativeTo(null);
zeichnen.setVisible(true);
}
@Override
public void paintComponent(Graphics maler) {
super.paintComponent(maler);
maler.drawImage(bildchen, 0, 0, this);
}
public static void main(String args[]) {
Runnable r = () -> {
new TestImageLoad();
};
SwingUtilities.invokeLater(r);
}
}

答案1

得分: 1

新代码的注释

类加载器对于加载应用程序资源非常有用,但要使它们正确工作有一些技巧:

(首先是个人的一些看法)我避免使用getResourceAsStream方法有很多原因。其中主要的原因包括:

  • getResource返回一个URL。当出现问题时,通过打印出URL并检查它是否指向我们认为的位置,可以更容易进行调试。如果资源没有找到,它将是null,但当我们在类路径中有两个同名资源并且它没有获取我们想要的那个资源时,它也可以发出警告。后者相当罕见,但确实会发生。
  • 如果我们需要从URL获取一个InputStream,那么只需要一个方法调用就可以了,非常简单。另一方面,许多方法使用URL或(表示)File等的String更好(甚至只能使用这些类型),或者使用BufferedInputStream(特别适用于Java Sound,可能还有其他情况),而从getResourceAsStream方法返回的流通常不是缓冲的。再次强调,修复这个问题只需要一行代码,但有时会让我们感到困惑。如果方法接受一个URL,那么这一切都会被自动处理。
  • 它们使用不同的规则来查找资源。这似乎是Sun/Oracle的一个愚蠢决定,但...无论如何。我通常建议人们在路径前加上/以确保它们直接从类路径的根目录开始。这对getResource很有效,但与getResourceAsStream不同。老实说,有人尝试解释这些差异的复杂性,但我不关心。getResource可用并且我就是用它。

好了,现在我解释了为什么我建议使用getResource,让我们回到技巧。

  1. 好吧,你已经使用了一个,前面提到过。就是通过添加/作为前缀。
  2. 但最重要的是,根类加载器和上下文类加载器之间的区别。我曾经试图在main方法中获取资源时遇到了无数麻烦(为主机JFrame设置图标)。事实证明,该调用使用的是系统或根类加载器,它只用于加载系统资源(例如JRE API类)。为了提高搜索速度,它的类路径非常有限。上下文类加载器是我们可以获取嵌入式资源的加载器。但是像TimeZone.class.getClassLoader()这样的调用通常会获取根类加载器,而不是上下文类加载器。要获取上下文类加载器,它总是在从自定义类的构造函数或方法中调用代码时起作用,并且使用实例化的对象(例如this)。

旧代码

原始代码将标签添加到了框架,但没有将绘制图像的自定义绘制面板添加进去!要做的方法是将自定义绘制面板添加到框架中,然后将标签添加到该面板上。

原始代码很混乱。我重新组织了它并在此过程中修复了许多问题。有太多事情我都忘了是什么。请参考这段代码以获取详细信息。

首先检查此代码是否在您的系统/环境上正常工作,然后使这段代码您的图像一起工作,似乎仍然存在问题。

import java.awt.Color;
import java.awt.Graphics;
import java.awt.image.BufferedImage;
import java.io.IOException;
import java.net.URL;
import javax.imageio.ImageIO;
import javax.swing.*;

public class TestImageLoad extends JPanel {

    JLabel face;
    BufferedImage bildchen;
    
    TestImageLoad() {
        initUI();
    }
    
    private void initUI() {
        JFrame zeichnen = new JFrame();
        face = new JLabel("face");
        face.setForeground(Color.RED);
        zeichnen.add(this);
        this.add(face);

        try {
            URL url = new URL("https://i.stack.imgur.com/OVOg3.jpg");
            bildchen = ImageIO.read(url);
        } catch (IOException e) {
            e.printStackTrace();
        }
        zeichnen.setDefaultCloseOperation(JFrame.DISPOSE_ON_CLOSE);
        zeichnen.pack();
        zeichnen.setLocationRelativeTo(null);
        zeichnen.setVisible(true);
    }

    @Override
    public void paintComponent(Graphics maler) {
        super.paintComponent(maler);
        maler.drawImage(bildchen, 0, 0, this);
    }

    public static void main(String args[]) {
        Runnable r = () -> {
            new TestImageLoad();
        };
        SwingUtilities.invokeLater(r);
    }
}
英文:

Comments on new (working) code

Class loaders are handy things for loading application resources, but there are a few tricks to getting them to work correctly:

(But first a personal aside) I avoid the getResourceAsStream method for a variety of reasons. Primary among them are:

  • getResource returns an URL. It's easier to debug when this goes wrong, as we can print out the URL and check it points where we think it should. If the resource was not found, it will be null, but it can also warn when we have two identically named resources in the class-path and it is not fetching the one we want. The latter is pretty rare, but it does happen.
  • If we need an InputStream from the URL, it is but a single method call. Easy. OTOH many methods work better (or even only) with a URL or (a String that represents) a File etc. or a BufferedInputStream (that applies to Java Sound particularly, possible other things) and the stream returned from the getResourceAsStream method is typically not buffered. Again, it is but a single line of code to fix that, but it can trip us up. And if the method takes an URL, that is all handled automatically.
  • They use different rules for finding resources. This seems like an idiotic decision by Sun/Oracle, but .. whatever. I typically advise people to prefix the path with / to ensure they are going directly from the root of the class-path. That works fine with getResource, but .. differently with getResourceAsStream. To be honest, people have tried to explain the intricacies of the differences, but I tune out. getResource works and that's what I use.

OK, now I've explained why I advised to use getResource, back into the tricks.

  1. Well, you'd already used one, mentioned above. Namely by adding / as a prefix.
  2. But most importantly, the difference between the root and context class loaders. I had uncountable troubles trying to fetch a resource within the main method (setting an icon for the host JFrame). It turned out that the call was using the system or root class loader which is only intended to load system (e.g. the JRE API classes) resources. It has a very limited class path in order to make searches faster. The context class loader is the one on which our embedded resources can be fetched. But a call like TimeZone.class.getClassLoader() will generally fetch the root class loader, not the context. To get the context class loader, it always works of the code is called from the constructor or a method of our custom class, and using an instantiated object (like this).

Old Code

The original code added the label to the frame, but not the custom painted panel that drew the image! The way to do it would be to add the custom painted panel to the frame, then add the label to that panel.

The original code was a mess. I reorganized it and fixed many things while doing so. So many things I've forgotten what. Instead refer to this code for details.

First check this code works on your system / environment, then get this code to work with your image, which still seems to be a problem.

import java.awt.Color;
import java.awt.Graphics;
import java.awt.image.BufferedImage;
import java.io.IOException;
import java.net.URL;
import javax.imageio.ImageIO;
import javax.swing.*;
public class TestImageLoad extends JPanel {
JLabel face;
BufferedImage bildchen;
TestImageLoad() {
initUI();
}
private void initUI() {
JFrame zeichnen = new JFrame();
face = new JLabel("face");
face.setForeground(Color.RED);
zeichnen.add(this);
this.add(face);
try {
URL url = new URL("https://i.stack.imgur.com/OVOg3.jpg");
bildchen = ImageIO.read(url);
} catch (IOException e) {
e.printStackTrace();
}
zeichnen.setDefaultCloseOperation(JFrame.DISPOSE_ON_CLOSE);
zeichnen.pack();
zeichnen.setLocationRelativeTo(null);
zeichnen.setVisible(true);
}
@Override
public void paintComponent(Graphics maler) {
super.paintComponent(maler);
maler.drawImage(bildchen, 0, 0, this);
}
public static void main(String args[]) {
Runnable r = () -> {
new TestImageLoad();
};
SwingUtilities.invokeLater(r);
}
}

huangapple
  • 本文由 发表于 2020年8月23日 00:57:31
  • 转载请务必保留本文链接:https://go.coder-hub.com/63538802.html
匿名

发表评论

匿名网友

:?: :razz: :sad: :evil: :!: :smile: :oops: :grin: :eek: :shock: :???: :cool: :lol: :mad: :twisted: :roll: :wink: :idea: :arrow: :neutral: :cry: :mrgreen:

确定