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

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 渲染效果和交互功能。