JEditorPaneHTMLEditorKitView视图

wen java案例 3

我来详细解释一下 JEditorPane、HTMLEditorKit 和对应的视图(View)机制。

JEditorPaneHTMLEditorKitView视图

JEditorPane + HTMLEditorKit 视图体系

核心组件关系

JEditorPane editor = new JEditorPane();
editor.setContentType("text/html");
editor.setText("<html><body><p>Hello World</p></body></html>");

当设置 HTML 内容时,内部会创建:

JEditorPane
  └── HTMLEditorKit (编辑器套件)
       └── ViewFactory (视图工厂)
            └── View (视图树)
                 ├── InlineView (行内元素)
                 ├── ParagraphView (段落)
                 ├── BlockView (块级元素)
                 └── ...

主要视图类型

// 1. 获取视图工厂
HTMLEditorKit kit = (HTMLEditorKit) editor.getEditorKit();
ViewFactory factory = kit.getViewFactory();
// 2. 获取文档的视图根
View rootView = editor.getUI().getRootView(editor);
// 3. 遍历视图树
public void traverseViews(View view, int depth) {
    for (int i = 0; i < depth; i++) System.out.print("  ");
    System.out.println(view.getClass().getSimpleName() + " - " + view.getAttributes());
    for (int i = 0; i < view.getViewCount(); i++) {
        traverseViews(view.getView(i), depth + 1);
    }
}

常见视图实现

视图类 用途 对应HTML元素
BlockView 块级元素 <div>, <p>, <h1>
InlineView 行内元素 <span>, <a>, <b>
ParagraphView 段落 <p>
TableView 表格 <table>
ImageView 图片 <img>
ListView 列表 <ul>, <ol>
ComponentView 组件嵌入 <object>, <applet>

获取特定元素的视图

// 通过位置获取视图
public View getViewAtPosition(JEditorPane editor, int position) {
    View root = editor.getUI().getRootView(editor);
    return getViewAtPositionImpl(root, position);
}
private View getViewAtPositionImpl(View view, int pos) {
    if (view.getStartOffset() <= pos && view.getEndOffset() > pos) {
        for (int i = 0; i < view.getViewCount(); i++) {
            View child = view.getView(i);
            View found = getViewAtPositionImpl(child, pos);
            if (found != null) return found;
        }
        return view;
    }
    return null;
}
// 使用示例
int caretPos = editor.getCaretPosition();
View viewAtCaret = getViewAtPosition(editor, caretPos);

自定义视图实现

// 1. 创建自定义视图
public class HighlightedParagraphView extends ParagraphView {
    private Color highlightColor;
    public HighlightedParagraphView(Element elem) {
        super(elem);
    }
    @Override
    public void paint(Graphics g, Shape allocation) {
        // 绘制背景高亮
        Rectangle rect = allocation.getBounds();
        if (highlightColor != null) {
            g.setColor(highlightColor);
            g.fillRect(rect.x, rect.y, rect.width, rect.height);
        }
        super.paint(g, allocation);
    }
}
// 2. 自定义视图工厂
public class CustomHTMLFactory extends HTMLEditorKit.HTMLFactory {
    @Override
    public View create(Element elem) {
        AttributeSet attrs = elem.getAttributes();
        Object tag = attrs.getAttribute(StyleConstants.NameAttribute);
        if (tag == HTML.Tag.P) {
            return new HighlightedParagraphView(elem);
        }
        return super.create(elem);
    }
}
// 3. 自定义编辑器套件
public class CustomHTMLEditorKit extends HTMLEditorKit {
    private final ViewFactory customFactory = new CustomHTMLFactory();
    @Override
    public ViewFactory getViewFactory() {
        return customFactory;
    }
}
// 4. 使用自定义套件
JEditorPane editor = new JEditorPane();
editor.setEditorKit(new CustomHTMLEditorKit());

视图性能优化

// 1. 延迟加载视图
public class LazyImageView extends ImageView {
    private boolean loaded = false;
    public LazyImageView(Element elem) {
        super(elem);
    }
    @Override
    public void paint(Graphics g, Shape allocation) {
        if (!loaded) {
            // 异步加载图片
            SwingUtilities.invokeLater(() -> {
                loadImage();
                loaded = true;
                repaint();
            });
            // 显示占位符
            drawPlaceholder(g, allocation);
            return;
        }
        super.paint(g, allocation);
    }
}
// 2. 视图缓存
public class CachedView extends InlineView {
    private Image cacheImage;
    private boolean dirty = true;
    @Override
    public void paint(Graphics g, Shape allocation) {
        if (dirty || cacheImage == null) {
            // 重新渲染到缓存
            cacheImage = renderToImage(allocation);
            dirty = false;
        }
        g.drawImage(cacheImage, allocation.getBounds().x, 
                   allocation.getBounds().y, null);
    }
    @Override
    public PreferenceSpan getPreferredSpan(int axis) {
        // 从缓存获取
        return new PreferenceSpan(1, 1);
    }
}

视图事件监听

// 监听视图变化
editor.getDocument().addDocumentListener(new DocumentListener() {
    @Override
    public void insertUpdate(DocumentEvent e) {
        // 视图树更新
        View root = editor.getUI().getRootView(editor);
        updateViews(root);
    }
    @Override
    public void removeUpdate(DocumentEvent e) {
        // 视图树更新
        View root = editor.getUI().getRootView(editor);
        updateViews(root);
    }
    @Override
    public void changedUpdate(DocumentEvent e) {}
    private void updateViews(View view) {
        view.preferenceChanged(view, true, true);
        for (int i = 0; i < view.getViewCount(); i++) {
            updateViews(view.getView(i));
        }
    }
});

实用工具方法

public class ViewUtils {
    // 获取元素对应的视图
    public static View getViewForElement(JEditorPane editor, Element elem) {
        View root = editor.getUI().getRootView(editor);
        return findViewForElement(root, elem);
    }
    private static View findViewForElement(View view, Element target) {
        if (view.getElement() == target) {
            return view;
        }
        for (int i = 0; i < view.getViewCount(); i++) {
            View found = findViewForElement(view.getView(i), target);
            if (found != null) return found;
        }
        return null;
    }
    // 获取视图中所有文本
    public static String getViewText(View view) {
        StringBuilder sb = new StringBuilder();
        if (view instanceof InlineView) {
            int start = view.getStartOffset();
            int end = view.getEndOffset();
            try {
                Document doc = view.getElement().getDocument();
                sb.append(doc.getText(start, end - start));
            } catch (BadLocationException e) {
                e.printStackTrace();
            }
        }
        for (int i = 0; i < view.getViewCount(); i++) {
            sb.append(getViewText(view.getView(i)));
        }
        return sb.toString();
    }
    // 调整视图大小
    public static void resizeViewToFit(View view) {
        float width = view.getPreferredSpan(View.X_AXIS);
        float height = view.getPreferredSpan(View.Y_AXIS);
        Shape allocation = new Rectangle(0, 0, (int)width, (int)height);
        view.setSize(width, height);
    }
}

这套视图机制是 Swing HTML 渲染的核心,通过理解和自定义这些视图,可以实现丰富的 HTML 渲染效果和交互功能。

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