我来详细解释一下 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 渲染效果和交互功能。
版权声明: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直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播