如何正确初始化JTextPane StyleSheet,因此该样式不会影响其他启用HTML的组件?

时间:2017-04-14 09:08:04

标签: java css swing jtextpane htmleditorkit

我尝试使用JTextPane呈现一些HTML并将CSS样式表应用于它。这意味着我正在使用HTMLEditorKitStyleSheet类。我知道所有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 + "&nbsp;<i>" + mynode.argument);
                }
            }
            return this;
        }

    }
}

rendered example

那么如何正确初始化这些对象,以便整个应用程序中没有CSS溢出(因此文本窗格根据CSS呈现,但树没有)?

注意:上图中的红色下划线表示溢出问题,稍后由我添加(不,它不是渲染器)。

1 个答案:

答案 0 :(得分:4)

我的代码中有问题的部分是调用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以使其返回样式表而不更改默认值。

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 + "&nbsp;<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);
        }

    }
}

fixed version