JLabel:异步加载 HTML 个图像
JLabel: load HTML images asynchronously
一个JLabel
允许HTML个内容,内容中可以包含一张图片:
String html = "<html><body>...<img src=\"http://some_url/image.png\"/>...</body></html>";
JLabel label = new JLabel(html);
请注意,我使用 JLabel
在 JXTreeTable
中渲染图像,因此 JLabel
的文本更新是在渲染器中的 EDT 上完成的。
问题是图像是同步加载的。如果服务器速度较慢,加载图像时 EDT 可能会被阻塞数秒。
我已经发现了为什么图像会同步加载,class 我需要修改以切换到图像的异步加载。
图像的加载是由 javax.swing.text.html.ImageView
class 完成的,它有一个方法 setLoadsSynchronously
.
问题是我不知道如何轻松调整 HTMLFactory
/ HTMLEditorKit
负责创建 ImageView
,并由 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)
使用 SwingWorkers(正如 trashgod 所建议的那样)是最好的解决方案。另一种选择是这样的:
创建一个下载图片的异步方法:
new Thread(new Runnable() {
public void run() {
//download the image
BufferedImage image = ImageIO.read(url);
//update the label
updateLabel(image)
}
}).start();
调用另一个方法来更新 EDT 中的标签
private void updateLabel(final BufferedImage image) {
SwingUtilities.invokeLater(new Runnable() {
public void run(){
// fine updating here... event dispatch thread
label.setIcon(new ImageIcon(image));
}
});
}
The image is only part of the HTML…calling setIcon
is not an option.
一种方法是在 SwingWorker
, save it temporarily to the file system, and reference the saved file in the <img/>
tag. The variation below, adapted from this example 的背景中加载图像,这是一个概念证明。您的实际实施可能会使用 SwingWorker<List<Row>, Row>
,其中每个 Row
包含一个图像 File
;您的 doInBackground()
实施将 publish()
临时结果可用;您对 process()
的实施将确保相关树-table 渲染器看到给定 Row
.
的正确 File
import java.awt.*;
import java.awt.image.*;
import java.io.*;
import java.net.URL;
import javax.imageio.ImageIO;
import javax.swing.*;
/**
* @see */
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);
}
}
}
一个JLabel
允许HTML个内容,内容中可以包含一张图片:
String html = "<html><body>...<img src=\"http://some_url/image.png\"/>...</body></html>";
JLabel label = new JLabel(html);
请注意,我使用 JLabel
在 JXTreeTable
中渲染图像,因此 JLabel
的文本更新是在渲染器中的 EDT 上完成的。
问题是图像是同步加载的。如果服务器速度较慢,加载图像时 EDT 可能会被阻塞数秒。
我已经发现了为什么图像会同步加载,class 我需要修改以切换到图像的异步加载。
图像的加载是由 javax.swing.text.html.ImageView
class 完成的,它有一个方法 setLoadsSynchronously
.
问题是我不知道如何轻松调整 HTMLFactory
/ HTMLEditorKit
负责创建 ImageView
,并由 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)
使用 SwingWorkers(正如 trashgod 所建议的那样)是最好的解决方案。另一种选择是这样的: 创建一个下载图片的异步方法:
new Thread(new Runnable() {
public void run() {
//download the image
BufferedImage image = ImageIO.read(url);
//update the label
updateLabel(image)
}
}).start();
调用另一个方法来更新 EDT 中的标签
private void updateLabel(final BufferedImage image) {
SwingUtilities.invokeLater(new Runnable() {
public void run(){
// fine updating here... event dispatch thread
label.setIcon(new ImageIcon(image));
}
});
}
The image is only part of the HTML…calling
setIcon
is not an option.
一种方法是在 SwingWorker
, save it temporarily to the file system, and reference the saved file in the <img/>
tag. The variation below, adapted from this example 的背景中加载图像,这是一个概念证明。您的实际实施可能会使用 SwingWorker<List<Row>, Row>
,其中每个 Row
包含一个图像 File
;您的 doInBackground()
实施将 publish()
临时结果可用;您对 process()
的实施将确保相关树-table 渲染器看到给定 Row
.
File
import java.awt.*;
import java.awt.image.*;
import java.io.*;
import java.net.URL;
import javax.imageio.ImageIO;
import javax.swing.*;
/**
* @see */
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);
}
}
}