本文目录导读:

在Java Swing开发中,JEditorPane 配合 HTMLEditorKit 处理HTML时,其底层解析器(Parser)是基于流(Stream)的,这涉及到如何将HTML文档作为输入流读取,并通过自定义或默认的解析器进行解析和渲染。
以下是关于 JEditorPane、HTMLEditorKit 及其解析器流处理的详细说明和实战示例:
核心组件与流处理机制
| 组件 | 作用 |
|---|---|
JEditorPane |
用于显示HTML的文本组件 |
HTMLEditorKit |
提供HTML编辑能力,包含解析器和视图工厂 |
Parser (解析器) |
读取HTML输入流,解析成文档结构 |
Document |
解析后的文档模型 |
Reader/InputStream |
读取HTML数据的输入流 |
默认解析器链:
InputStream → InputStreamReader → HTMLEditorKit.Parser → HTMLDocument
基本流处理示例
1 从文件读取HTML并显示
import javax.swing.*;
import javax.swing.text.html.*;
import java.io.*;
public class HTMLStreamExample {
public static void main(String[] args) {
JFrame frame = new JFrame("HTML Stream Example");
JEditorPane editorPane = new JEditorPane();
editorPane.setEditable(false);
editorPane.setContentType("text/html");
// 使用HTMLEditorKit
HTMLEditorKit kit = new HTMLEditorKit();
editorPane.setEditorKit(kit);
// 创建HTML文档
HTMLDocument doc = (HTMLDocument) kit.createDefaultDocument();
editorPane.setDocument(doc);
try {
// 从文件读取HTML流
File htmlFile = new File("example.html");
FileInputStream fis = new FileInputStream(htmlFile);
InputStreamReader reader = new InputStreamReader(fis, "UTF-8");
// 直接将流读入文档
kit.read(reader, doc, 0);
reader.close();
// 或者使用setPage方法(内部也会使用流)
// editorPane.setPage(htmlFile.toURI().toURL());
} catch (Exception e) {
e.printStackTrace();
}
frame.add(new JScrollPane(editorPane));
frame.setSize(600, 400);
frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
frame.setVisible(true);
}
}
自定义Parser与流处理
1 扩展HTMLEditorKit自定义解析器
import javax.swing.text.html.*;
import javax.swing.text.html.parser.*;
import java.io.*;
public class CustomHTMLEditorKit extends HTMLEditorKit {
@Override
public HTMLEditorKit.Parser getParser() {
return new CustomParser();
}
private static class CustomParser extends HTMLEditorKit.Parser {
@Override
public void parse(Reader r, ParserCallback cb, boolean ignoreCharSet)
throws IOException {
System.out.println("[CustomParser] 开始解析HTML流...");
// 包装Reader以便于监控流处理
BufferedReader br = new BufferedReader(r);
// 创建默认的解析器
ParserDelegator delegator = new ParserDelegator();
// 自定义回调包装
ParserCallback wrappedCallback = new ParserCallbackWrapper(cb);
// 委托给默认解析器
delegator.parse(br, wrappedCallback, ignoreCharSet);
System.out.println("[CustomParser] 解析完成!");
}
}
// 记录解析过程的回调包装器
private static class ParserCallbackWrapper extends ParserCallback {
private ParserCallback original;
public ParserCallbackWrapper(ParserCallback original) {
this.original = original;
}
@Override
public void handleText(char[] text, int pos) {
System.out.println("[解析] 文本: " + new String(text).trim());
original.handleText(text, pos);
}
@Override
public void handleStartTag(HTML.Tag t, MutableAttributeSet a, int pos) {
System.out.println("[解析] 开始标签: " + t + " at " + pos);
original.handleStartTag(t, a, pos);
}
@Override
public void handleEndTag(HTML.Tag t, int pos) {
System.out.println("[解析] 结束标签: " + t);
original.handleEndTag(t, pos);
}
@Override
public void handleSimpleTag(HTML.Tag t, MutableAttributeSet a, int pos) {
System.out.println("[解析] 自闭合标签: " + t);
original.handleSimpleTag(t, a, pos);
}
@Override
public void flush() throws BadLocationException {
original.flush();
}
}
}
2 使用自定义解析器
public class CustomParserDemo {
public static void main(String[] args) throws Exception {
JFrame frame = new JFrame("Custom Parser Demo");
JEditorPane editorPane = new JEditorPane();
// 使用自定义EditorKit
CustomHTMLEditorKit kit = new CustomHTMLEditorKit();
editorPane.setEditorKit(kit);
// HTML内容(作为字符串流)
String html = "<html><body><h1>Hello</h1><p>This is <b>bold</b> text.</p></body></html>";
StringReader reader = new StringReader(html);
// 解析流
HTMLDocument doc = (HTMLDocument) kit.createDefaultDocument();
kit.read(reader, doc, 0);
editorPane.setDocument(doc);
frame.add(new JScrollPane(editorPane));
frame.setSize(400, 300);
frame.setVisible(true);
}
}
高级流处理技巧
1 带监控的流包装器
import java.io.*;
public class MonitoredInputStream extends FilterInputStream {
private long totalBytesRead = 0;
private long lastReportTime = System.currentTimeMillis();
public MonitoredInputStream(InputStream in) {
super(in);
}
@Override
public int read() throws IOException {
int b = super.read();
if (b != -1) {
totalBytesRead++;
reportProgress();
}
return b;
}
@Override
public int read(byte[] b, int off, int len) throws IOException {
int bytesRead = super.read(b, off, len);
if (bytesRead > 0) {
totalBytesRead += bytesRead;
reportProgress();
}
return bytesRead;
}
private void reportProgress() {
long now = System.currentTimeMillis();
if (now - lastReportTime > 1000) { // 每秒报告一次
System.out.println("已读取: " + (totalBytesRead / 1024) + " KB");
lastReportTime = now;
}
}
public long getTotalBytesRead() {
return totalBytesRead;
}
}
2 流转换:从网络获取HTML
import java.net.*;
import javax.swing.text.html.*;
public class NetworkHTMLReader {
public static void loadHTMLFromURL(JEditorPane editorPane, String urlString) {
try {
URL url = new URL(urlString);
HttpURLConnection conn = (HttpURLConnection) url.openConnection();
conn.setRequestMethod("GET");
// 获取输入流
InputStream inputStream = conn.getInputStream();
// 使用监控流
MonitoredInputStream monitoredStream = new MonitoredInputStream(inputStream);
// 转换为Reader
InputStreamReader reader = new InputStreamReader(monitoredStream, "UTF-8");
// 解析
HTMLEditorKit kit = (HTMLEditorKit) editorPane.getEditorKit();
HTMLDocument doc = (HTMLDocument) kit.createDefaultDocument();
kit.read(reader, doc, 0);
reader.close();
System.out.println("总共读取: " + monitoredStream.getTotalBytesRead() + " bytes");
} catch (Exception e) {
e.printStackTrace();
}
}
}
流处理优化建议
1 使用BufferedReader提高性能
// 不推荐
InputStreamReader reader = new InputStreamReader(inputStream);
kit.read(reader, doc, 0);
// 推荐 - 使用缓冲
BufferedReader reader = new BufferedReader(
new InputStreamReader(inputStream, "UTF-8"), 8192); // 8KB缓冲区
kit.read(reader, doc, 0);
2 处理字符编码问题
public static void loadHTMLWithEncoding(JEditorPane pane, InputStream is, String encoding) {
try {
// 先检测BOM标记
PushbackInputStream pushback = new PushbackInputStream(is, 4);
byte[] bom = new byte[4];
int n = pushback.read(bom, 0, 4);
String detectedEncoding = encoding;
if (n >= 2) {
if (bom[0] == (byte)0xFE && bom[1] == (byte)0xFF) {
detectedEncoding = "UTF-16BE";
pushback.unread(bom, 2, n - 2);
} else if (bom[0] == (byte)0xFF && bom[1] == (byte)0xFE) {
detectedEncoding = "UTF-16LE";
pushback.unread(bom, 2, n - 2);
} else if (n >= 3 && bom[0] == (byte)0xEF &&
bom[1] == (byte)0xBB && bom[2] == (byte)0xBF) {
detectedEncoding = "UTF-8";
pushback.unread(bom, 3, n - 3);
} else {
pushback.unread(bom, 0, n);
}
}
Reader reader = new BufferedReader(
new InputStreamReader(pushback, detectedEncoding));
HTMLEditorKit kit = (HTMLEditorKit) pane.getEditorKit();
HTMLDocument doc = (HTMLDocument) kit.createDefaultDocument();
kit.read(reader, doc, 0);
} catch (Exception e) {
e.printStackTrace();
}
}
注意事项和最佳实践
- 线程安全:解析过程应在EDT(事件分发线程)外进行,完成后更新UI
- 资源管理:始终在finally块中关闭流
- 错误处理:HTML解析错误不会抛出异常,但文档可能不完整
- 性能监控:大型HTML文件推荐使用缓冲区监控
// 在后台线程中加载HTML
SwingWorker<Void, Void> worker = new SwingWorker<Void, Void>() {
@Override
protected Void doInBackground() throws Exception {
// 在后台线程中解析
HTMLDocument doc = (HTMLDocument) kit.createDefaultDocument();
kit.read(reader, doc, 0);
return null;
}
@Override
protected void done() {
// 在EDT中更新UI
editorPane.setDocument(doc);
}
};
worker.execute();
通过合理使用流处理,你可以高效地从各种来源(文件、网络、内存)加载HTML内容到 JEditorPane 中显示,并且可以根据需要进行自定义解析和监控。
版权声明:NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
足球直播
足球直播
足球直播
篮球直播
篮球直播
篮球直播
NBA直播
NBA直播
NBA直播
CBA直播
CBA直播
CBA直播
亚冠直播
亚冠直播
亚冠直播
欧冠直播
欧冠直播
欧冠直播
英超直播
英超直播
英超直播
西甲直播
西甲直播
西甲直播
中超直播
中超直播
中超直播
足球直播
足球直播
足球直播
篮球直播
篮球直播
篮球直播
NBA直播
NBA直播
NBA直播
CBA直播
CBA直播
CBA直播
亚冠直播
亚冠直播
亚冠直播
欧冠直播
欧冠直播
欧冠直播
英超直播
英超直播
英超直播
西甲直播
西甲直播
西甲直播
中超直播
中超直播
中超直播
足球直播
足球直播
足球直播
篮球直播
篮球直播
篮球直播
NBA直播
NBA直播
NBA直播
CBA直播
CBA直播
CBA直播
亚冠直播
亚冠直播
亚冠直播
欧冠直播
欧冠直播
欧冠直播
英超直播
英超直播
英超直播
西甲直播
西甲直播
西甲直播
中超直播
中超直播
中超直播
足球直播
足球直播
足球直播
篮球直播
篮球直播
篮球直播
NBA直播
NBA直播
NBA直播
CBA直播
CBA直播
CBA直播
亚冠直播
亚冠直播
亚冠直播
欧冠直播
欧冠直播
欧冠直播
英超直播
英超直播
英超直播
西甲直播
西甲直播
西甲直播
中超直播
中超直播
中超直播
曼联直播
曼联直播
曼联直播
日职联直播
日职联直播
日职联直播
澳超直播
澳超直播
澳超直播
阿森纳直播
阿森纳直播
阿森纳直播
足球直播
足球直播
足球直播
NBA直播
NBA直播
NBA直播
曼联直播
曼联直播
曼联直播
日职联直播
日职联直播
日职联直播
澳超直播
澳超直播
澳超直播
阿森纳直播
阿森纳直播
阿森纳直播
足球直播
足球直播
足球直播
NBA直播
NBA直播
NBA直播
曼联直播
曼联直播
曼联直播
日职联直播
日职联直播
日职联直播
澳超直播
澳超直播
澳超直播
阿森纳直播
阿森纳直播
阿森纳直播
足球直播
足球直播
足球直播
NBA直播
NBA直播
NBA直播
曼联直播
曼联直播
曼联直播
日职联直播
日职联直播
日职联直播
澳超直播
澳超直播
澳超直播
阿森纳直播
阿森纳直播
阿森纳直播
足球直播
足球直播
足球直播
NBA直播
NBA直播
NBA直播
欧冠直播
欧冠直播
欧冠直播
欧冠直播
欧冠直播
欧冠直播
欧冠直播
欧冠直播
欧冠直播
欧冠直播
欧冠直播
欧冠直播
欧冠直播
欧冠直播
欧冠直播
欧冠直播
欧冠直播
欧冠直播
欧冠直播
欧冠直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
足球直播
足球直播
足球直播
足球直播
足球直播
英超直播
英超直播
英超直播
英超直播
英超直播
篮球直播
篮球直播
篮球直播
篮球直播
篮球直播
CBA直播
CBA直播
CBA直播
CBA直播
CBA直播
中超直播
中超直播
中超直播
中超直播
中超直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
足球直播
足球直播
足球直播
足球直播
足球直播
英超直播
英超直播
英超直播
英超直播
英超直播
篮球直播
篮球直播
篮球直播
篮球直播
篮球直播
CBA直播
CBA直播
CBA直播
CBA直播
CBA直播
中超直播
中超直播
中超直播
中超直播
中超直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播