本文目录导读:

看起来你遇到了 JEditorPane 与 HTMLEditorKit 在解析 HTML 时出现的 “Parser method cancelled” 错误,这通常与 取消(Cancel)键 或解析器被中断有关。
问题原因
JEditorPane 使用 HTMLEditorKit 解析 HTML 时,内部会启动一个 Parser 线程,当用户按下 Esc 键 或通过其他方式触发取消操作时,解析器可能会被中断,抛出类似 “Parser method cancelled” 的异常。
常见场景
- 用户按 Esc 键 - 中断了正在进行的 HTML 解析
- 更新时 - 旧的解析任务被取消,新任务开始
- 异步加载 - 在 SwingWorker 或后台线程中修改文档内容
解决方案
方案 1:捕获并忽略异常(推荐)
import javax.swing.*;
import javax.swing.text.html.*;
import java.awt.*;
public class HtmlEditorCancelFix extends JFrame {
public HtmlEditorCancelFix() {
JEditorPane editorPane = new JEditorPane();
editorPane.setContentType("text/html");
editorPane.setEditable(false);
// 关键:捕获异常,避免影响 GUI
// 可以通过自定义 HTMLEditorKit 来实现
editorPane.setEditorKit(new SafeHtmlEditorKit());
// 设置 HTML 内容
editorPane.setText("<html><body><h1>Hello World</h1>" +
"<p>Some long content that might trigger cancellation...</p></body></html>");
JScrollPane scrollPane = new JScrollPane(editorPane);
add(scrollPane, BorderLayout.CENTER);
setSize(400, 300);
setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
setLocationRelativeTo(null);
}
// 自定义安全的 HTMLEditorKit
private static class SafeHtmlEditorKit extends HTMLEditorKit {
@Override
public Document createDefaultDocument() {
return new SafeHtmlDocument();
}
}
private static class SafeHtmlDocument extends HTMLDocument {
@Override
public void setParser(HtmlDocument.HTMLReader reader) {
try {
super.setParser(reader);
} catch (Exception e) {
// 忽略所有解析相关异常,包括 cancelled 异常
System.err.println("Parser cancelled ignored: " + e.getMessage());
}
}
}
public static void main(String[] args) {
SwingUtilities.invokeLater(() -> new HtmlEditorCancelFix().setVisible(true));
}
}
方案 2:禁用 Esc 键取消
如果你确认不需要取消功能,可以禁用 Esc 键:
// 在初始化时添加按键拦截
editorPane.getInputMap(JComponent.WHEN_ANCESTOR_OF_FOCUSED_COMPONENT)
.put(KeyStroke.getKeyStroke("ESCAPE"), "none");
editorPane.getActionMap().put("none", new AbstractAction() {
@Override
public void actionPerformed(ActionEvent e) {
// 不做任何操作
}
});
方案 3:使用代理模式包装解析器
更彻底的解决方案是重写 HTMLEditorKit 的解析器:
public class CancellationSafeHtmlEditorKit extends HTMLEditorKit {
@Override
public ViewFactory getViewFactory() {
return new HTMLFactory() {
@Override
public View create(Element elem) {
// 可以在创建 View 时添加保护
return super.create(elem);
}
};
}
@Override
public void read(Reader in, Document doc, int pos) throws IOException, BadLocationException {
try {
super.read(in, doc, pos);
} catch (Exception e) {
if (e.getMessage() != null &&
e.getMessage().contains("cancelled")) {
// 静默处理取消异常
return;
}
throw e; // 其他异常继续抛出
}
}
}
方案 4:使用 SwingUtilities.invokeLater
更新在 EDT(事件调度线程)中执行:
SwingUtilities.invokeLater(() -> {
try {
editorPane.setText(htmlContent);
} catch (Exception e) {
// 忽略 cancelled 异常
if (!e.getMessage().contains("cancelled")) {
e.printStackTrace();
}
}
});
完整示例:生产环境可用的解决方案
import javax.swing.*;
import javax.swing.text.*;
import javax.swing.text.html.*;
import java.awt.*;
import java.io.*;
public class JEditorPaneCancellationFixDemo extends JFrame {
private JEditorPane editorPane;
public JEditorPaneCancellationFixDemo() {
setTitle("JEditorPane 取消异常处理");
setSize(600, 400);
setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
// 使用自定义编辑器套件
editorPane = new JEditorPane();
editorPane.setEditorKit(new CancellationSafeKit());
editorPane.setEditable(false);
// 设置示例内容
editorPane.setText(loadExampleHtml());
JScrollPane scrollPane = new JScrollPane(editorPane);
add(scrollPane, BorderLayout.CENTER);
// 按钮演示:加载新内容可能触发取消
JButton loadButton = new JButton("加载复杂 HTML");
loadButton.addActionListener(e -> {
SwingUtilities.invokeLater(() -> {
editorPane.setText(generateComplexHtml());
});
});
add(loadButton, BorderLayout.SOUTH);
}
private String loadExampleHtml() {
return "<html><body>" +
"<h2>JEditorPane 取消异常演示</h2>" +
"<p>按下 Esc 键或快速切换内容可能触发 'Parser method cancelled' 异常。</p>" +
"<p>本演示捕获并处理了该异常。</p>" +
"</body></html>";
}
private String generateComplexHtml() {
StringBuilder sb = new StringBuilder();
sb.append("<html><body>");
for (int i = 0; i < 100; i++) {
sb.append("<div style='border:1px solid #ccc; padding:10px; margin:5px;'>")
.append("<h3>Section ").append(i).append("</h3>")
.append("<p>Content with <b>bold</b>, <i>italic</i>, and ")
.append("<span style='color:red;'>colored text</span></p>")
.append("</div>");
}
sb.append("</body></html>");
return sb.toString();
}
// 安全的编辑器套件
private static class CancellationSafeKit extends HTMLEditorKit {
@Override
public void read(Reader in, Document doc, int pos)
throws IOException, BadLocationException {
try {
super.read(in, doc, pos);
} catch (IOException | BadLocationException e) {
if (isCancellationException(e)) {
// 记录日志但不中断程序
System.err.println("解析取消(安全忽略)");
} else {
throw e;
}
} catch (Exception e) {
if (isCancellationException(e)) {
System.err.println("解析取消(安全忽略)");
} else {
throw new IOException("HTML 解析错误", e);
}
}
}
private boolean isCancellationException(Throwable e) {
String msg = e.getMessage();
return msg != null &&
(msg.contains("cancelled") || msg.contains("Canceled"));
}
}
public static void main(String[] args) {
SwingUtilities.invokeLater(() -> {
try {
UIManager.setLookAndFeel(UIManager.getSystemLookAndFeel());
} catch (Exception e) {
e.printStackTrace();
}
new JEditorPaneCancellationFixDemo().setVisible(true);
});
}
}
| 方案 | 优点 | 缺点 |
|---|---|---|
| 捕获异常 | 实现简单,不影响现有功能 | 异常仍会打印到控制台 |
| 禁用 Esc 键 | 快速解决问题 | 失去了用户期望的取消功能 |
| 重写 EditorKit | 最彻底,可定制性强 | 实现复杂度较高 |
| invokeLater | 符合 Swing 线程模型 | 仅适用于 EDT 问题 |
推荐做法:结合使用 方案 1(捕获异常)+ 方案 2(可选禁用 Esc 键),既能保持功能完整,又能避免程序崩溃。
如果问题仍然存在,请检查是否使用了自定义的 HTMLEditorKit 或 Document 实现,可能需要在自定义代码中添加相应的异常处理。