gpt4 book ai didi

Java Swing ImageIcon 未在 JLabel 中显示

转载 作者:行者123 更新时间:2023-12-02 08:52:54 25 4
gpt4 key购买 nike

我发现以下内容MRE每次执行时都会重现相同的错误:

import java.awt.Component;
import java.awt.Graphics;
import java.awt.GraphicsConfiguration;
import java.awt.GraphicsDevice;
import java.awt.GraphicsEnvironment;
import java.awt.Image;
import java.awt.MediaTracker;
import java.awt.image.BufferedImage;
import java.util.Objects;
import javax.swing.Icon;
import javax.swing.ImageIcon;
import javax.swing.JFrame;
import javax.swing.JLabel;

public class IconDelegate implements Icon {
private final Icon delegated;

public IconDelegate(final Icon delegated) {
this.delegated = Objects.requireNonNull(delegated);
}

@Override
public int getIconWidth() {
return delegated.getIconWidth();
}

@Override
public int getIconHeight() {
return delegated.getIconHeight();
}

@Override
public void paintIcon(final Component c, final Graphics g, final int x, final int y) {
delegated.paintIcon(c, g, x, y);
//Some other drawing operations would be here...
}

public static void main(final String[] args) {
final ImageIcon ii = new ImageIcon();
final GraphicsEnvironment genv = GraphicsEnvironment.getLocalGraphicsEnvironment();
final GraphicsDevice gdev = genv.getDefaultScreenDevice();
final GraphicsConfiguration gcnf = gdev.getDefaultConfiguration();
final BufferedImage bimg = gcnf.createCompatibleImage(300, 400);
final Image img = bimg.getScaledInstance(150, 200, Image.SCALE_SMOOTH);
//img.getWidth(null); //Uncomment this line and the image will be drawn normally!
////img.flush(); //Uncomment this line and the image will not be drawn.
ii.setImage(img);
System.out.println(ii.getImageLoadStatus() == MediaTracker.ABORTED);
final JLabel lbl = new JLabel(new IconDelegate(ii));
final JFrame frame = new JFrame("ImageIcon delegate.");
frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
frame.getContentPane().add(lbl);
frame.pack();
frame.setLocationRelativeTo(null);
frame.setVisible(true);
}
}

我希望任何人都可以重现的错误是 ImageIcon的图像永远不会绘制在显示的标签/框架中。

当我尝试调试它时,我发现图像加载状态( ImageIcon )等于 MediaTracker.ABORTED其委托(delegate)给 ImageObserver.ABORT目标Component (实现 ImageObserver ) MediaTracker ImageIcon的实例,这又(根据 ImageObserver 的文档)与其 imageUpdate 有关。方法。

我还注意到,如果我调用 img.getWidth(null);就在将图像设置为 ImageIcon 之前然后图像正常绘制(即错误停止再现)并且图像加载状态等于MediaTracker.ABORTED不再了。阅读 ImageObserver.imageUpdate 的文档后我见过的方法指出 Image.getWidth(ImageObserver)方法是异步?...

我还注意到,如果我调用 img.flush(); img.getWidth(null);之后调用,然后错误重现。调用img.getWidth(null);刷新图像后,错误将不再重现。这种模式可以重复。因此,我归结为我在 MediaTracker 的文档中找到的可能最重要的信息。 :

If no ImageObservers are observing the image when the first frame has finished loading, the image might flush itself to conserve resources (see Image.flush()).

我必须指出,我确实关心事件的顺序。例如我需要 ImageIcon创建为空,然后使用setImage更新其图像。我这么说是因为,正如我注意到的,创建 ImageIcon并在构建时为其提供图像似乎可以正确绘制图像而不是重现错误。查看 ImageIcon 的差异后的构造函数接受 ImageImageIcon.setImage我发现构造函数还从图像中请求属性“comment”,提供 Image.getProperty方法 ImageObserver ...所以我也猜测它是异步?...

我还打过System.out.println(img.getClass());但它最终出现在sun.*中的一个类中包含编译代码但没有文档的包。所以我没能跟上。

我希望我的调试努力不会产生误导。

所以我的问题是:

  1. 什么原因导致 MRE 中未绘制图像?
  2. 如何避免发生此错误,而不必进行任意调用(例如 img.getWidth(null); 之前的 setImage )?

我还发现了this相关问题,但我看到 ImageIcon类内部使用 MediaTracker那么重复代码有什么意义呢?原因MediaTracker ImageIcon的我不知道中止图像的加载(根据其加载状态)。

编辑 1

更现实且不太最小的 MRE 仍然会重现错误,如下所示:

import java.awt.BorderLayout;
import java.awt.Color;
import java.awt.Component;
import java.awt.Graphics;
import java.awt.GridBagLayout;
import java.awt.Image;
import java.awt.image.BufferedImage;
import java.io.IOException;
import java.util.Objects;
import javax.imageio.ImageIO;
import javax.swing.Icon;
import javax.swing.ImageIcon;
import javax.swing.JButton;
import javax.swing.JFileChooser;
import javax.swing.JFrame;
import javax.swing.JLabel;
import javax.swing.JPanel;
import javax.swing.SwingUtilities;

public class ImageThumbnail implements Icon {
private final ImageIcon delegated;
private final int w, h;
private String text;

public ImageThumbnail(final int width, final int height, final String text) {
w = width;
h = height;
this.text = Objects.toString(text).trim();
delegated = new ImageIcon();
}

@Override
public int getIconWidth() {
return w;
}

@Override
public int getIconHeight() {
return h;
}

public void setText(final String text) {
this.text = Objects.toString(text).trim();
}

//Scales the given image to fit this icon's width and height maintaining aspect ratio:
public void setImage(final BufferedImage bimg) {
final int bimgw = bimg.getWidth(),
bimgh = bimg.getHeight();
if (bimgw > bimgh)
delegated.setImage(bimg.getScaledInstance(Math.min(w, bimgw), -1, Image.SCALE_SMOOTH));
else
delegated.setImage(bimg.getScaledInstance(-1, Math.min(h, bimgh), Image.SCALE_SMOOTH));
//Alternatively you can try the following code if you don't trust the -1 arguments above:
//delegated.setImage(bimg.getScaledInstance(w, h, Image.SCALE_SMOOTH));
}

@Override
public void paintIcon(final Component c, final Graphics g, final int x, final int y) {
delegated.paintIcon(c, g, x, y);
//Other drawing operations here, like drawing a String, at the center, over the ImageIcon:
g.drawString(text, x + w / 2 - g.getFontMetrics().stringWidth(text) / 2, y + h / 2);
}

private static JPanel centered(final Component c) {
/*Creating a JPanel with GridBagLayout and a single component
in it, will layout the component in the center of the JPanel.*/
final JPanel panel = new JPanel(new GridBagLayout());
panel.add(c);
return panel;
}

public static void main(final String[] args) {
SwingUtilities.invokeLater(() -> {

final ImageThumbnail thumb = new ImageThumbnail(200, 200, "Select an image...");

final JLabel lbl = new JLabel(thumb);

final JFileChooser chooser = new JFileChooser();
chooser.setFileSelectionMode(JFileChooser.FILES_ONLY);
chooser.setMultiSelectionEnabled(false);

final JButton load = new JButton("Load image");
load.addActionListener(e -> {
if (chooser.showOpenDialog(load) == JFileChooser.APPROVE_OPTION) {
try {
thumb.setImage(ImageIO.read(chooser.getSelectedFile()));
thumb.setText("Image-001");
lbl.setForeground(Color.GREEN.darker());
//lbl.repaint(); //Not needed, as I call 'lbl.setForeground(Color.GREEN.darker());' above...
}
catch (final IOException iox) {
iox.printStackTrace();
}
}
});

final JPanel contents = new JPanel(new BorderLayout());
contents.add(centered(lbl), BorderLayout.CENTER);
contents.add(centered(load), BorderLayout.PAGE_END);

final JFrame frame = new JFrame("Thumbnail of images.");
frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
frame.getContentPane().add(contents);
frame.pack();
frame.setLocationRelativeTo(null);
frame.setVisible(true);
});
}
}

我知道我可以使用SwingWorker在按钮上加载图像,这样用户就不会遇到延迟,但为了让事情更简单,我更喜欢阻止 EDT 一段时间。

最佳答案

How to prevent this error from happening without having to make arbitrary calls

使用您的原始代码,我得到了 NPE。

以下似乎有效。我现在看到一个黑色图像:

//final ImageIcon ii = new ImageIcon();

//ii.setImage(img);
ImageIcon ii = new ImageIcon( img );

编辑:

我尝试使用不同的方法,包括确保标签具有 ImageObserver。对我来说没有任何作用。

import java.awt.*;
import javax.swing.*;
import javax.swing.text.*;
import java.awt.image.*;

public class IconMRE extends JPanel
{
private ImageIcon icon = new ImageIcon( new BufferedImage(200, 200, BufferedImage.TYPE_INT_RGB) );
private JLabel label1 = new JLabel( icon );

public IconMRE()
{
setLayout( new BorderLayout(20, 20) );

label1 = new JLabel( icon );
add(label1, BorderLayout.LINE_START);

JLabel label2 = new JLabel();
icon.setImageObserver( label2 );
label2.setIcon( icon );
add(label2, BorderLayout.LINE_END);


JComboBox<Color>comboBox = new JComboBox<Color>();
comboBox.addItem( Color.RED );
comboBox.addItem( Color.GREEN );
comboBox.addItem( Color.BLUE );
add(comboBox, BorderLayout.PAGE_START);

comboBox.addActionListener( (e) ->
{
Color background = (Color)comboBox.getSelectedItem();
BufferedImage image = new BufferedImage(200, 200, BufferedImage.TYPE_INT_RGB);
Graphics2D g2d = image.createGraphics();
g2d.setColor( background );
g2d.fillRect(0, 0, 200, 200);
g2d.dispose();
// image.getWidth(null);
icon.setImage( image );
// label1.repaint();
});

comboBox.setSelectedIndex(2);
}

private static void createAndShowUI()
{
JFrame frame = new JFrame("IconMRE");
frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
frame.add( new IconMRE() );
frame.pack();
frame.setLocationRelativeTo( null );
frame.setVisible( true );
}

public static void main(String[] args)
{
EventQueue.invokeLater(new Runnable()
{
public void run()
{
createAndShowUI();
}
});
}
}

唯一有效的是在标签上强制重绘()。

关于Java Swing ImageIcon 未在 JLabel 中显示,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/60672272/

25 4 0
Copyright 2021 - 2024 cfsdn All Rights Reserved 蜀ICP备2022000587号
广告合作:1813099741@qq.com 6ren.com