java - JLabel:异步加载 HTML 图像

标签 java swing jlabel

JLabel 允许 HTML 内容,其中可以包含图像:

String html = "<html><body>...<img src=\"http://some_url/image.png\"/>...</body></html>";
JLabel label = new JLabel(html);

请注意,我使用 JLabelJXTreeTable 中渲染图像,因此 JLabel 文本的更新是在渲染器中的 EDT。

问题在于图像是同步加载的。如果服务器速度较慢,则在加载图像时 EDT 可能会被阻塞数秒。

我已经发现了为什么图像会被同步加载,以及我需要修改哪个类才能切换到图像的异步加载。

图像的加载是由javax.swing.text.html.ImageView类完成的,该类有一个方法setLoadsSynchronously

问题是我不知道如何轻松调整负责创建 ImageViewHTMLFactory/HTMLEditorKit ,并且它由 JLabel 内部使用。 让事情变得更加复杂的是,我需要一个适用于所有外观和感觉的解决方案。

如果以上内容不清楚,以下线程转储显示了 EDT 在图像检索期间被阻止的内容:

"AWT-EventQueue-0@999" prio=6 tid=0x10 nid=NA waiting
  java.lang.Thread.State: WAITING
      at java.lang.Object.wait(Object.java:-1)
      at java.awt.MediaTracker.waitForID(MediaTracker.java:677)
      at javax.swing.ImageIcon.loadImage(ImageIcon.java:314)
      at javax.swing.ImageIcon.setImage(ImageIcon.java:381)
      at javax.swing.text.html.ImageView.loadImage(ImageView.java:704)
      at javax.swing.text.html.ImageView.refreshImage(ImageView.java:673)
      at javax.swing.text.html.ImageView.sync(ImageView.java:645)
      at javax.swing.text.html.ImageView.getPreferredSpan(ImageView.java:443)
      at javax.swing.text.FlowView$LogicalView.getPreferredSpan(FlowView.java:732)
      at javax.swing.text.FlowView.calculateMinorAxisRequirements(FlowView.java:233)
      at javax.swing.text.ParagraphView.calculateMinorAxisRequirements(ParagraphView.java:717)
      at javax.swing.text.html.ParagraphView.calculateMinorAxisRequirements(ParagraphView.java:157)
      at javax.swing.text.BoxView.checkRequests(BoxView.java:935)
      at javax.swing.text.BoxView.getMinimumSpan(BoxView.java:568)
      at javax.swing.text.html.ParagraphView.getMinimumSpan(ParagraphView.java:270)
      at javax.swing.text.BoxView.calculateMinorAxisRequirements(BoxView.java:903)
      at javax.swing.text.html.BlockView.calculateMinorAxisRequirements(BlockView.java:146)
      at javax.swing.text.BoxView.checkRequests(BoxView.java:935)
      at javax.swing.text.BoxView.getMinimumSpan(BoxView.java:568)
      at javax.swing.text.html.BlockView.getMinimumSpan(BlockView.java:378)
      at javax.swing.text.BoxView.calculateMinorAxisRequirements(BoxView.java:903)
      at javax.swing.text.html.BlockView.calculateMinorAxisRequirements(BlockView.java:146)
      at javax.swing.text.BoxView.checkRequests(BoxView.java:935)
      at javax.swing.text.BoxView.getMinimumSpan(BoxView.java:568)
      at javax.swing.text.html.BlockView.getMinimumSpan(BlockView.java:378)
      at javax.swing.text.BoxView.calculateMinorAxisRequirements(BoxView.java:903)
      at javax.swing.text.html.BlockView.calculateMinorAxisRequirements(BlockView.java:146)
      at javax.swing.text.BoxView.checkRequests(BoxView.java:935)
      at javax.swing.text.BoxView.getPreferredSpan(BoxView.java:545)
      at javax.swing.text.html.BlockView.getPreferredSpan(BlockView.java:362)
      at javax.swing.plaf.basic.BasicHTML$Renderer.<init>(BasicHTML.java:383)
      at javax.swing.plaf.basic.BasicHTML.createHTMLView(BasicHTML.java:67)
      at javax.swing.plaf.basic.BasicHTML.updateRenderer(BasicHTML.java:207)
      at javax.swing.plaf.basic.BasicLabelUI.propertyChange(BasicLabelUI.java:417)
      at javax.swing.plaf.synth.SynthLabelUI.propertyChange(SynthLabelUI.java:296)
      at java.beans.PropertyChangeSupport.fire(PropertyChangeSupport.java:335)
      at java.beans.PropertyChangeSupport.firePropertyChange(PropertyChangeSupport.java:327)
      at java.beans.PropertyChangeSupport.firePropertyChange(PropertyChangeSupport.java:263)
      at java.awt.Component.firePropertyChange(Component.java:8428)
      at org.jdesktop.swingx.renderer.JRendererLabel.firePropertyChange(JRendererLabel.java:292)
      at javax.swing.JLabel.setText(JLabel.java:330)

最佳答案

The image is only part of the HTML…calling setIcon is not an option.

一种方法是在 SwingWorker 的背景中加载图像。 ,暂时保存到文件系统中,并在 <img/> 中引用保存的文件标签。下面的变体改编自此 example ,是一个概念证明。您的实际实现可能会使用 SwingWorker<List<Row>, Row> ,其中每个Row包含图像 File ;你的doInBackground()实现将 publish()中期结果公布后;您执行 process()将确保相关的树表渲染器看到正确的 File对于给定的Row .

import java.awt.*;
import java.awt.image.*;
import java.io.*;
import java.net.URL;
import javax.imageio.ImageIO;
import javax.swing.*;

/**
 * @see http://stackoverflow.com/questions/4530659 */
public class WorkerTest extends JFrame {

    private JPanel panel = new JPanel();
    private JLabel label = new JLabel("Loading...");

    public WorkerTest() {
        this.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
        label.setHorizontalTextPosition(JLabel.CENTER);
        label.setVerticalTextPosition(JLabel.CENTER);
        this.add(label);
        this.pack();
        this.setLocationRelativeTo(null);
    }

    private void start() {
        new ImageWorker().execute();
    }

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

            @Override
            public void run() {
                WorkerTest wt = new WorkerTest();
                wt.setVisible(true);
                wt.start();
            }
        });
    }

    class ImageWorker extends SwingWorker<File, Void> {

        private static final String TEST =
            "http://cdn.sstatic.net/stackexchange/img/logos/so/so-logo.png";
        private BufferedImage image;
        private File file;

        @Override
        protected File doInBackground() throws IOException {
            image = ImageIO.read(new URL(TEST));
            file = File.createTempFile("image", null);
            ImageIO.write(image, "png", file);
            return file;
        }

        @Override
        protected void done() {
            label.setText("<html><body><img src=\"file://"
                + file.getAbsolutePath() + "\"/></body></html>");
            panel.setPreferredSize(new Dimension(image.getWidth(), image.getHeight()));
            WorkerTest.this.pack();
            WorkerTest.this.setLocationRelativeTo(null);
        }
    }
}

关于java - JLabel:异步加载 HTML 图像,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/44944203/

相关文章:

java - 系统.输出.打印。如何对打印输出进行编号? ( java )

java - 当另一方使用 readUTF/writeUTF 时如何用 netty 读/写?

java - JPanel尺寸问题

java - JPasswordField 位于图像顶部

Java JLabel - 添加唯一标识符

java - BoxLayout 添加左边距

java - 尝试连接到本地 SMTP 服务器时 Camel 出现连接异常

java - 2 "linked"对象

java - 实现与 ActionListener 交互的方法?

java - 获取固定大小的 GridLayout