如何正确初始化 JTextPane StyleSheet,使其他 HTML 启用的组件不受该样式的影响?
How does one properly initialize a JTextPane StyleSheet, so no other HTML enabled component is affected by the style?
我正在尝试使用 JTextPane
呈现一些 HTML 并对其应用 CSS 样式表。这意味着我正在使用 HTMLEditorKit
和 StyleSheet
类。我知道所有 HTMLEditorKits
共享相同的默认 StyleSheet
实例,因此如果您更改此默认样式表对象,您将在应用程序级别应用更改(呈现 HTML 的所有组件)。
但在我的示例中,我认为我已经通过基于默认创建自己的 StyleSheet
实例来避免这种情况。然而,这不起作用,如显示的 JTree
所示,它按照仅打算应用于 JTextPane
.
的样式表呈现
import java.awt.*;
import javax.swing.*;
import javax.swing.text.html.*;
import javax.swing.tree.*;
public class TextPaneCssSpill extends JFrame {
private JTextPane textPane;
private JTree tree;
private JSplitPane splitPane;
public TextPaneCssSpill() {
HTMLEditorKit hed = new HTMLEditorKit();
StyleSheet defaultStyle = hed.getStyleSheet();
StyleSheet style = new StyleSheet();
style.addStyleSheet(defaultStyle);
style.addRule("body {font-family:\"Monospaced\"; font-size:9px;}");
style.addRule("i {color:#bababa; font-size:9px;}"); // gray italic
hed.setStyleSheet(style);
textPane = new JTextPane();
textPane.setEditorKit(hed);
textPane.setDocument(hed.createDefaultDocument());
DefaultMutableTreeNode root = new DefaultMutableTreeNode(new MyNode("name", "argument"), true);
root.add(new DefaultMutableTreeNode(new MyNode("name", "argument"), false));
root.add(new DefaultMutableTreeNode(new MyNode("name", "argument"), false));
root.add(new DefaultMutableTreeNode(new MyNode("name", "argument"), false));
root.add(new DefaultMutableTreeNode(new MyNode("name", "argument"), false));
tree = new JTree(root);
tree.setCellRenderer(new MyNodeTreeRenderer());
setLayout(new BorderLayout());
splitPane = new JSplitPane(JSplitPane.VERTICAL_SPLIT, textPane, tree);
add(splitPane);
pack();
setLocationRelativeTo(null);
}
public static void main(String[] args) {
SwingUtilities.invokeLater(new Runnable() {
@Override
public void run() {
new TextPaneCssSpill().setVisible(true);
}
});
}
private static class MyNode {
private final String name;
private final String argument;
public MyNode(String name, String argument) {
this.name = name;
this.argument = argument;
}
@Override
public String toString() {
return name + " " + argument;
}
}
private static class MyNodeTreeRenderer extends DefaultTreeCellRenderer {
@Override
public Component getTreeCellRendererComponent(JTree tree, Object value, boolean sel, boolean expanded, boolean leaf, int row, boolean hasFocus) {
super.getTreeCellRendererComponent(tree, value, sel, expanded, leaf, row, hasFocus);
if (value instanceof DefaultMutableTreeNode) {
DefaultMutableTreeNode node = (DefaultMutableTreeNode) value;
if (node.getUserObject() instanceof MyNode) {
MyNode mynode = (MyNode) node.getUserObject();
setText("<html>" + mynode.name + " <i>" + mynode.argument);
}
}
return this;
}
}
}
那么如何正确地初始化这些对象,以便 CSS 不会在应用程序中溢出(以便文本窗格根据 CSS 呈现,而树则不会)?
注:上图中红色下划线表示溢出问题,是我后来添加的(不是,不是渲染器)。
我的代码中有问题的部分是调用 HTMLEditorKit.setStyleSheet(style);
。这取代了 all HTMLEditorKits
的默认样式表实例,这是我不知道的。
/**
* Set the set of styles to be used to render the various
* HTML elements. These styles are specified in terms of
* CSS specifications. Each document produced by the kit
* will have a copy of the sheet which it can add the
* document specific styles to. By default, the StyleSheet
* specified is shared by all HTMLEditorKit instances.
* This should be reimplemented to provide a finer granularity
* if desired.
*/
public void setStyleSheet(StyleSheet s) {
if (s == null) {
AppContext.getAppContext().remove(DEFAULT_STYLES_KEY);
} else {
AppContext.getAppContext().put(DEFAULT_STYLES_KEY, s);
}
}
/**
* Get the set of styles currently being used to render the
* HTML elements. By default the resource specified by
* DEFAULT_CSS gets loaded, and is shared by all HTMLEditorKit
* instances.
*/
public StyleSheet getStyleSheet() {
AppContext appContext = AppContext.getAppContext();
StyleSheet defaultStyles = (StyleSheet) appContext.get(DEFAULT_STYLES_KEY);
if (defaultStyles == null) {
defaultStyles = new StyleSheet();
appContext.put(DEFAULT_STYLES_KEY, defaultStyles);
try {
InputStream is = HTMLEditorKit.getResourceAsStream(DEFAULT_CSS);
Reader r = new BufferedReader(
new InputStreamReader(is, "ISO-8859-1"));
defaultStyles.loadRules(r, null);
r.close();
} catch (Throwable e) {
// on error we simply have no styles... the html
// will look mighty wrong but still function.
}
}
return defaultStyles;
}
所以需要做的是扩展 HTMLEditorKit
使其成为 return 您的样式表而不更改默认值。
import java.awt.*;
import java.io.IOException;
import javax.swing.*;
import javax.swing.text.*;
import javax.swing.text.html.*;
import javax.swing.tree.*;
public class TextPaneCssSpill extends JFrame {
private JTextPane textPane;
private JTree tree;
private JSplitPane splitPane;
private StyleSheet style;
public TextPaneCssSpill() {
MyHTMLEditorKit hed = new MyHTMLEditorKit();
StyleSheet defaultStyle = hed.getDefaultStyleSheet();
style = new StyleSheet();
style.addStyleSheet(defaultStyle);
style.addRule("body {font-family:\"Monospaced\"; font-size:9px;}");
style.addRule("i {color:#bababa; font-size:9px;}"); // gray italic
hed.setStyleSheet(style);
textPane = new JTextPane();
textPane.setEditorKit(hed);
textPane.setDocument(hed.createDefaultDocument());
appendHtmlToTextPane("<i>our gray italic text</i>", textPane);
DefaultMutableTreeNode root = new DefaultMutableTreeNode(new MyNode("name", "argument"), true);
root.add(new DefaultMutableTreeNode(new MyNode("name", "argument"), false));
root.add(new DefaultMutableTreeNode(new MyNode("name", "argument"), false));
root.add(new DefaultMutableTreeNode(new MyNode("name", "argument"), false));
root.add(new DefaultMutableTreeNode(new MyNode("name", "argument"), false));
tree = new JTree(root);
tree.setCellRenderer(new MyNodeTreeRenderer());
setDefaultCloseOperation(EXIT_ON_CLOSE);
setLayout(new BorderLayout());
splitPane = new JSplitPane(JSplitPane.VERTICAL_SPLIT, textPane, tree);
add(splitPane);
pack();
setLocationRelativeTo(null);
}
private void appendHtmlToTextPane(String str, JTextPane pane) {
Document doc = pane.getDocument();
if (doc != null) {
if (doc instanceof HTMLDocument) {
HTMLDocument htmlDoc = (HTMLDocument) doc;
Element html = htmlDoc.getDefaultRootElement();
if (HTML.Tag.HTML.toString().equalsIgnoreCase(html.getName())) {
Element body = null;
for (int i = 0; i < html.getElementCount(); i++) {
Element element = html.getElement(i);
if (element.getAttributes().getAttribute(StyleConstants.NameAttribute) == HTML.Tag.BODY) {
body = element;
break;
}
}
if (HTML.Tag.BODY.toString().equalsIgnoreCase(body.getName())) {
try {
htmlDoc.insertBeforeEnd(body, str);
Element lastLine = body.getElement(body.getElementCount() - 1);
int end = lastLine.getStartOffset();
textPane.setCaretPosition(end);
} catch (BadLocationException e) {
} catch (IOException ex) {
}
}
}
}
}
}
public static void main(String[] args) {
SwingUtilities.invokeLater(new Runnable() {
@Override
public void run() {
new TextPaneCssSpill().setVisible(true);
}
});
}
private static class MyNode {
private final String name;
private final String argument;
public MyNode(String name, String argument) {
this.name = name;
this.argument = argument;
}
@Override
public String toString() {
return name + " " + argument;
}
}
private static class MyNodeTreeRenderer extends DefaultTreeCellRenderer {
@Override
public Component getTreeCellRendererComponent(JTree tree, Object value, boolean sel, boolean expanded, boolean leaf, int row, boolean hasFocus) {
super.getTreeCellRendererComponent(tree, value, sel, expanded, leaf, row, hasFocus);
if (value instanceof DefaultMutableTreeNode) {
DefaultMutableTreeNode node = (DefaultMutableTreeNode) value;
if (node.getUserObject() instanceof MyNode) {
MyNode mynode = (MyNode) node.getUserObject();
setText("<html>" + mynode.name + " <i>" + mynode.argument);
}
}
return this;
}
}
/**
* Avoid setting the stylesheet for all HTMLEditorKit instances.
*/
private static class MyHTMLEditorKit extends HTMLEditorKit {
private StyleSheet myStyle;
@Override
public StyleSheet getStyleSheet() {
return myStyle == null ? super.getStyleSheet() : myStyle;
}
@Override
public void setStyleSheet(StyleSheet s) {
this.myStyle = s;
}
public StyleSheet getDefaultStyleSheet() {
return super.getStyleSheet();
}
public void setDefaultStyleSheet(StyleSheet s) {
super.setStyleSheet(s);
}
}
}
我正在尝试使用 JTextPane
呈现一些 HTML 并对其应用 CSS 样式表。这意味着我正在使用 HTMLEditorKit
和 StyleSheet
类。我知道所有 HTMLEditorKits
共享相同的默认 StyleSheet
实例,因此如果您更改此默认样式表对象,您将在应用程序级别应用更改(呈现 HTML 的所有组件)。
但在我的示例中,我认为我已经通过基于默认创建自己的 StyleSheet
实例来避免这种情况。然而,这不起作用,如显示的 JTree
所示,它按照仅打算应用于 JTextPane
.
import java.awt.*;
import javax.swing.*;
import javax.swing.text.html.*;
import javax.swing.tree.*;
public class TextPaneCssSpill extends JFrame {
private JTextPane textPane;
private JTree tree;
private JSplitPane splitPane;
public TextPaneCssSpill() {
HTMLEditorKit hed = new HTMLEditorKit();
StyleSheet defaultStyle = hed.getStyleSheet();
StyleSheet style = new StyleSheet();
style.addStyleSheet(defaultStyle);
style.addRule("body {font-family:\"Monospaced\"; font-size:9px;}");
style.addRule("i {color:#bababa; font-size:9px;}"); // gray italic
hed.setStyleSheet(style);
textPane = new JTextPane();
textPane.setEditorKit(hed);
textPane.setDocument(hed.createDefaultDocument());
DefaultMutableTreeNode root = new DefaultMutableTreeNode(new MyNode("name", "argument"), true);
root.add(new DefaultMutableTreeNode(new MyNode("name", "argument"), false));
root.add(new DefaultMutableTreeNode(new MyNode("name", "argument"), false));
root.add(new DefaultMutableTreeNode(new MyNode("name", "argument"), false));
root.add(new DefaultMutableTreeNode(new MyNode("name", "argument"), false));
tree = new JTree(root);
tree.setCellRenderer(new MyNodeTreeRenderer());
setLayout(new BorderLayout());
splitPane = new JSplitPane(JSplitPane.VERTICAL_SPLIT, textPane, tree);
add(splitPane);
pack();
setLocationRelativeTo(null);
}
public static void main(String[] args) {
SwingUtilities.invokeLater(new Runnable() {
@Override
public void run() {
new TextPaneCssSpill().setVisible(true);
}
});
}
private static class MyNode {
private final String name;
private final String argument;
public MyNode(String name, String argument) {
this.name = name;
this.argument = argument;
}
@Override
public String toString() {
return name + " " + argument;
}
}
private static class MyNodeTreeRenderer extends DefaultTreeCellRenderer {
@Override
public Component getTreeCellRendererComponent(JTree tree, Object value, boolean sel, boolean expanded, boolean leaf, int row, boolean hasFocus) {
super.getTreeCellRendererComponent(tree, value, sel, expanded, leaf, row, hasFocus);
if (value instanceof DefaultMutableTreeNode) {
DefaultMutableTreeNode node = (DefaultMutableTreeNode) value;
if (node.getUserObject() instanceof MyNode) {
MyNode mynode = (MyNode) node.getUserObject();
setText("<html>" + mynode.name + " <i>" + mynode.argument);
}
}
return this;
}
}
}
那么如何正确地初始化这些对象,以便 CSS 不会在应用程序中溢出(以便文本窗格根据 CSS 呈现,而树则不会)?
注:上图中红色下划线表示溢出问题,是我后来添加的(不是,不是渲染器)。
我的代码中有问题的部分是调用 HTMLEditorKit.setStyleSheet(style);
。这取代了 all HTMLEditorKits
的默认样式表实例,这是我不知道的。
/**
* Set the set of styles to be used to render the various
* HTML elements. These styles are specified in terms of
* CSS specifications. Each document produced by the kit
* will have a copy of the sheet which it can add the
* document specific styles to. By default, the StyleSheet
* specified is shared by all HTMLEditorKit instances.
* This should be reimplemented to provide a finer granularity
* if desired.
*/
public void setStyleSheet(StyleSheet s) {
if (s == null) {
AppContext.getAppContext().remove(DEFAULT_STYLES_KEY);
} else {
AppContext.getAppContext().put(DEFAULT_STYLES_KEY, s);
}
}
/**
* Get the set of styles currently being used to render the
* HTML elements. By default the resource specified by
* DEFAULT_CSS gets loaded, and is shared by all HTMLEditorKit
* instances.
*/
public StyleSheet getStyleSheet() {
AppContext appContext = AppContext.getAppContext();
StyleSheet defaultStyles = (StyleSheet) appContext.get(DEFAULT_STYLES_KEY);
if (defaultStyles == null) {
defaultStyles = new StyleSheet();
appContext.put(DEFAULT_STYLES_KEY, defaultStyles);
try {
InputStream is = HTMLEditorKit.getResourceAsStream(DEFAULT_CSS);
Reader r = new BufferedReader(
new InputStreamReader(is, "ISO-8859-1"));
defaultStyles.loadRules(r, null);
r.close();
} catch (Throwable e) {
// on error we simply have no styles... the html
// will look mighty wrong but still function.
}
}
return defaultStyles;
}
所以需要做的是扩展 HTMLEditorKit
使其成为 return 您的样式表而不更改默认值。
import java.awt.*;
import java.io.IOException;
import javax.swing.*;
import javax.swing.text.*;
import javax.swing.text.html.*;
import javax.swing.tree.*;
public class TextPaneCssSpill extends JFrame {
private JTextPane textPane;
private JTree tree;
private JSplitPane splitPane;
private StyleSheet style;
public TextPaneCssSpill() {
MyHTMLEditorKit hed = new MyHTMLEditorKit();
StyleSheet defaultStyle = hed.getDefaultStyleSheet();
style = new StyleSheet();
style.addStyleSheet(defaultStyle);
style.addRule("body {font-family:\"Monospaced\"; font-size:9px;}");
style.addRule("i {color:#bababa; font-size:9px;}"); // gray italic
hed.setStyleSheet(style);
textPane = new JTextPane();
textPane.setEditorKit(hed);
textPane.setDocument(hed.createDefaultDocument());
appendHtmlToTextPane("<i>our gray italic text</i>", textPane);
DefaultMutableTreeNode root = new DefaultMutableTreeNode(new MyNode("name", "argument"), true);
root.add(new DefaultMutableTreeNode(new MyNode("name", "argument"), false));
root.add(new DefaultMutableTreeNode(new MyNode("name", "argument"), false));
root.add(new DefaultMutableTreeNode(new MyNode("name", "argument"), false));
root.add(new DefaultMutableTreeNode(new MyNode("name", "argument"), false));
tree = new JTree(root);
tree.setCellRenderer(new MyNodeTreeRenderer());
setDefaultCloseOperation(EXIT_ON_CLOSE);
setLayout(new BorderLayout());
splitPane = new JSplitPane(JSplitPane.VERTICAL_SPLIT, textPane, tree);
add(splitPane);
pack();
setLocationRelativeTo(null);
}
private void appendHtmlToTextPane(String str, JTextPane pane) {
Document doc = pane.getDocument();
if (doc != null) {
if (doc instanceof HTMLDocument) {
HTMLDocument htmlDoc = (HTMLDocument) doc;
Element html = htmlDoc.getDefaultRootElement();
if (HTML.Tag.HTML.toString().equalsIgnoreCase(html.getName())) {
Element body = null;
for (int i = 0; i < html.getElementCount(); i++) {
Element element = html.getElement(i);
if (element.getAttributes().getAttribute(StyleConstants.NameAttribute) == HTML.Tag.BODY) {
body = element;
break;
}
}
if (HTML.Tag.BODY.toString().equalsIgnoreCase(body.getName())) {
try {
htmlDoc.insertBeforeEnd(body, str);
Element lastLine = body.getElement(body.getElementCount() - 1);
int end = lastLine.getStartOffset();
textPane.setCaretPosition(end);
} catch (BadLocationException e) {
} catch (IOException ex) {
}
}
}
}
}
}
public static void main(String[] args) {
SwingUtilities.invokeLater(new Runnable() {
@Override
public void run() {
new TextPaneCssSpill().setVisible(true);
}
});
}
private static class MyNode {
private final String name;
private final String argument;
public MyNode(String name, String argument) {
this.name = name;
this.argument = argument;
}
@Override
public String toString() {
return name + " " + argument;
}
}
private static class MyNodeTreeRenderer extends DefaultTreeCellRenderer {
@Override
public Component getTreeCellRendererComponent(JTree tree, Object value, boolean sel, boolean expanded, boolean leaf, int row, boolean hasFocus) {
super.getTreeCellRendererComponent(tree, value, sel, expanded, leaf, row, hasFocus);
if (value instanceof DefaultMutableTreeNode) {
DefaultMutableTreeNode node = (DefaultMutableTreeNode) value;
if (node.getUserObject() instanceof MyNode) {
MyNode mynode = (MyNode) node.getUserObject();
setText("<html>" + mynode.name + " <i>" + mynode.argument);
}
}
return this;
}
}
/**
* Avoid setting the stylesheet for all HTMLEditorKit instances.
*/
private static class MyHTMLEditorKit extends HTMLEditorKit {
private StyleSheet myStyle;
@Override
public StyleSheet getStyleSheet() {
return myStyle == null ? super.getStyleSheet() : myStyle;
}
@Override
public void setStyleSheet(StyleSheet s) {
this.myStyle = s;
}
public StyleSheet getDefaultStyleSheet() {
return super.getStyleSheet();
}
public void setDefaultStyleSheet(StyleSheet s) {
super.setStyleSheet(s);
}
}
}