JEditorPaneHTMLEditorKitParserMethodCancelled取消键

wen java案例 1

本文目录导读:

JEditorPaneHTMLEditorKitParserMethodCancelled取消键

  1. 问题原因
  2. 解决方案
  3. 完整示例:生产环境可用的解决方案

看起来你遇到了 JEditorPaneHTMLEditorKit 在解析 HTML 时出现的 “Parser method cancelled” 错误,这通常与 取消(Cancel)键 或解析器被中断有关。

问题原因

JEditorPane 使用 HTMLEditorKit 解析 HTML 时,内部会启动一个 Parser 线程,当用户按下 Esc 键 或通过其他方式触发取消操作时,解析器可能会被中断,抛出类似 “Parser method cancelled” 的异常。

常见场景

  1. 用户按 Esc 键 - 中断了正在进行的 HTML 解析
  2. 更新时 - 旧的解析任务被取消,新任务开始
  3. 异步加载 - 在 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 键),既能保持功能完整,又能避免程序崩溃。

如果问题仍然存在,请检查是否使用了自定义的 HTMLEditorKitDocument 实现,可能需要在自定义代码中添加相应的异常处理。

抱歉,评论功能暂时关闭!