具有自定义文档和编辑器的 JTextPane 无法正确显示图标

发布于 2024-12-11 10:59:44 字数 7624 浏览 0 评论 0原文

我们正在用 Java 实现一个非常小的类似于信使的应用程序,并使用 JTextPane 作为我们的消息面板。现在,我们正在尝试用 .gif 文件替换常见的笑脸,并且我们使用了一些可以完成这项工作的示例......只不过图标是居中对齐的,这很烦人。有没有办法让它看起来内联(左对齐,或正常)?一些解决这个问题的帮助将不胜感激。

这是初始化代码:

messagePane = new JTextPane();
messagePane.setEditable(false);
messagePane.setEditorKit(new WrapHTMLEditorKit());
messagePane.setContentType("text/html");
MessageDocument doc = new MessageDocument();
doc.addDocumentListener(new SmileyDocumentListener(messagePane));
messagePane.setStyledDocument(doc);

MessageDocument 类:

public class MessageDocument extends HTMLDocument {

    static private String NEW_LINE = System.getProperty("line.separator");

    static private SimpleAttributeSet DEFAULT_STYLE = new SimpleAttributeSet();
    static private SimpleAttributeSet ANNOUNCE_GLOBAL_STYLE = new SimpleAttributeSet();
    static private SimpleAttributeSet MESSAGE_GLOBAL_STYLE = new SimpleAttributeSet();
    static private SimpleAttributeSet MESSAGE_PRIVATE_STYLE = new SimpleAttributeSet();
    static private SimpleAttributeSet ERROR_STYLE = new SimpleAttributeSet();

    static{
        StyleConstants.setFontFamily(DEFAULT_STYLE, "Courier New");
        StyleConstants.setItalic(DEFAULT_STYLE, true);
        StyleConstants.setForeground(DEFAULT_STYLE, Color.black);       

        StyleConstants.setFontFamily(ANNOUNCE_GLOBAL_STYLE, "Courier New");
        StyleConstants.setBold(ANNOUNCE_GLOBAL_STYLE, true);
        StyleConstants.setForeground(ANNOUNCE_GLOBAL_STYLE, new Color(0, 100, 0));      

        StyleConstants.setFontFamily(MESSAGE_GLOBAL_STYLE, "Courier New");
        StyleConstants.setForeground(MESSAGE_GLOBAL_STYLE, Color.blue);

        StyleConstants.setFontFamily(MESSAGE_PRIVATE_STYLE, "Courier New");
        StyleConstants.setForeground(MESSAGE_PRIVATE_STYLE, Color.black);
        StyleConstants.setBackground(MESSAGE_PRIVATE_STYLE, new Color(220, 220, 255));

        StyleConstants.setFontFamily(ERROR_STYLE, "Courier New");
        StyleConstants.setForeground(ERROR_STYLE, Color.yellow);
        StyleConstants.setBackground(ERROR_STYLE, Color.red);

    }


    public MessageDocument() {
        super();
    }

    public void addMessage(ServerMessage msg) {
        SimpleAttributeSet style;

        if (MessageType.ANNOUNCE_GLOBAL.equals(msg.getType()) || MessageType.CONNECTION_EVENT.equals(msg.getType()) || MessageType.DISCONNECTION_EVENT.equals(msg.getType())) {
            style = ANNOUNCE_GLOBAL_STYLE;
        } else if (MessageType.MESSAGE_GLOBAL.equals(msg.getType())) {
            style = MESSAGE_GLOBAL_STYLE;
        } else if (MessageType.MESSAGE_PRIVATE.equals(msg.getType())) {
            style = MESSAGE_PRIVATE_STYLE;
        } else if (MessageType.ERROR.equals(msg.getType())) {
            style = ERROR_STYLE;
        } else {
            style = DEFAULT_STYLE;
        }

        try {
            insertString(getLength(), msg.getMessage() + NEW_LINE, style);          
        } catch (BadLocationException e) {
            e.printStackTrace();
        }
    }
}

WrapHTMLEditorKit 类:

public class WrapHTMLEditorKit extends HTMLEditorKit {

    @Override
    public ViewFactory getViewFactory() {

        return new HTMLFactory() {
            public View create(Element e) {
                View v = super.create(e);
                if (v instanceof InlineView) {
                    return new InlineView(e) {
                        public int getBreakWeight(int axis, float pos, float len) {
                            return GoodBreakWeight;
                        }

                        public View breakView(int axis, int p0, float pos, float len) {
                            if (axis == View.X_AXIS) {
                                checkPainter();
                                int p1 = getGlyphPainter().getBoundedPosition(this, p0, pos, len);
                                if (p0 == getStartOffset() && p1 == getEndOffset()) {
                                    return this;
                                }
                                return createFragment(p0, p1);
                            }
                            return this;
                        }
                    };
                } else if (v instanceof ParagraphView) {
                    return new ParagraphView(e) {
                        protected SizeRequirements calculateMinorAxisRequirements(int axis, SizeRequirements r) {
                            if (r == null) {
                                r = new SizeRequirements();
                            }
                            float pref = layoutPool.getPreferredSpan(axis);
                            float min = layoutPool.getMinimumSpan(axis);
                            // Don't include insets, Box.getXXXSpan will include them.
                            r.minimum = (int) min;
                            r.preferred = Math.max(r.minimum, (int) pref);
                            r.maximum = Integer.MAX_VALUE;
                            r.alignment = 0.5f;
                            return r;
                        }

                    };
                }
                return v;
            }
        };
    }
}

最后是 SmileyDocumentListener 类:

public class SmileyDocumentListener implements DocumentListener {

    private JTextComponent owner;

    private HashMap<String,ImageIcon> smileMap;

    public SmileyDocumentListener(JTextComponent owner) {
        this.owner = owner;
        this.smileMap = new HashMap<String, ImageIcon>();

        this.smileMap.put(":)", new ImageIcon("resources/images/smileys/smile.gif"));
    }

    @Override
    public void insertUpdate(DocumentEvent event) {
        final DocumentEvent e = event;
        SwingUtilities.invokeLater(new Runnable() {
            public void run() {
                if (e.getDocument() instanceof StyledDocument) {
                    try {
                        StyledDocument doc=(StyledDocument)e.getDocument();
                        int start = Utilities.getRowStart(owner, Math.max(0, e.getOffset() - 1));
                        int end = Utilities.getWordStart(owner, e.getOffset() + e.getLength());
                        String text = doc.getText(start, end-start);

                        for (String token : smileMap.keySet()) {
                            int i = text.indexOf(token);
                            while (i >= 0) {
                                final SimpleAttributeSet attrs = new SimpleAttributeSet(doc.getCharacterElement(start + i).getAttributes());
                                if (StyleConstants.getIcon(attrs) == null) {
                                    StyleConstants.setIcon(attrs, smileMap.get(token));
                                    doc.remove(start + i, 2);
                                    doc.insertString(start + i, token, attrs);
                                }
                                i = text.indexOf(token, i+token.length());
                            }
                        }
                    } catch (BadLocationException e1) {
                        e1.printStackTrace();
                    }
                }
            }
        });
    }

    @Override
    public void removeUpdate(DocumentEvent e) {}

    @Override
    public void changedUpdate(DocumentEvent e) {}

}

注意:我尝试将 r.alignment = 0.5f; 更改为 0.0f WrapHTMLEditorKit.java

We are implementing a very small messenger like application in Java and are using a JTextPane for our messages panel. Now, we are trying to replace common smileys with .gif files and we use some example that does the job... except that the icons are center aligned and it's annoying. Is there anyway to make it look inline (left align, or normal)? Some help to resolve this would be appreciated.

Here's the initialisation code :

messagePane = new JTextPane();
messagePane.setEditable(false);
messagePane.setEditorKit(new WrapHTMLEditorKit());
messagePane.setContentType("text/html");
MessageDocument doc = new MessageDocument();
doc.addDocumentListener(new SmileyDocumentListener(messagePane));
messagePane.setStyledDocument(doc);

The MessageDocument class :

public class MessageDocument extends HTMLDocument {

    static private String NEW_LINE = System.getProperty("line.separator");

    static private SimpleAttributeSet DEFAULT_STYLE = new SimpleAttributeSet();
    static private SimpleAttributeSet ANNOUNCE_GLOBAL_STYLE = new SimpleAttributeSet();
    static private SimpleAttributeSet MESSAGE_GLOBAL_STYLE = new SimpleAttributeSet();
    static private SimpleAttributeSet MESSAGE_PRIVATE_STYLE = new SimpleAttributeSet();
    static private SimpleAttributeSet ERROR_STYLE = new SimpleAttributeSet();

    static{
        StyleConstants.setFontFamily(DEFAULT_STYLE, "Courier New");
        StyleConstants.setItalic(DEFAULT_STYLE, true);
        StyleConstants.setForeground(DEFAULT_STYLE, Color.black);       

        StyleConstants.setFontFamily(ANNOUNCE_GLOBAL_STYLE, "Courier New");
        StyleConstants.setBold(ANNOUNCE_GLOBAL_STYLE, true);
        StyleConstants.setForeground(ANNOUNCE_GLOBAL_STYLE, new Color(0, 100, 0));      

        StyleConstants.setFontFamily(MESSAGE_GLOBAL_STYLE, "Courier New");
        StyleConstants.setForeground(MESSAGE_GLOBAL_STYLE, Color.blue);

        StyleConstants.setFontFamily(MESSAGE_PRIVATE_STYLE, "Courier New");
        StyleConstants.setForeground(MESSAGE_PRIVATE_STYLE, Color.black);
        StyleConstants.setBackground(MESSAGE_PRIVATE_STYLE, new Color(220, 220, 255));

        StyleConstants.setFontFamily(ERROR_STYLE, "Courier New");
        StyleConstants.setForeground(ERROR_STYLE, Color.yellow);
        StyleConstants.setBackground(ERROR_STYLE, Color.red);

    }


    public MessageDocument() {
        super();
    }

    public void addMessage(ServerMessage msg) {
        SimpleAttributeSet style;

        if (MessageType.ANNOUNCE_GLOBAL.equals(msg.getType()) || MessageType.CONNECTION_EVENT.equals(msg.getType()) || MessageType.DISCONNECTION_EVENT.equals(msg.getType())) {
            style = ANNOUNCE_GLOBAL_STYLE;
        } else if (MessageType.MESSAGE_GLOBAL.equals(msg.getType())) {
            style = MESSAGE_GLOBAL_STYLE;
        } else if (MessageType.MESSAGE_PRIVATE.equals(msg.getType())) {
            style = MESSAGE_PRIVATE_STYLE;
        } else if (MessageType.ERROR.equals(msg.getType())) {
            style = ERROR_STYLE;
        } else {
            style = DEFAULT_STYLE;
        }

        try {
            insertString(getLength(), msg.getMessage() + NEW_LINE, style);          
        } catch (BadLocationException e) {
            e.printStackTrace();
        }
    }
}

The WrapHTMLEditorKit class :

public class WrapHTMLEditorKit extends HTMLEditorKit {

    @Override
    public ViewFactory getViewFactory() {

        return new HTMLFactory() {
            public View create(Element e) {
                View v = super.create(e);
                if (v instanceof InlineView) {
                    return new InlineView(e) {
                        public int getBreakWeight(int axis, float pos, float len) {
                            return GoodBreakWeight;
                        }

                        public View breakView(int axis, int p0, float pos, float len) {
                            if (axis == View.X_AXIS) {
                                checkPainter();
                                int p1 = getGlyphPainter().getBoundedPosition(this, p0, pos, len);
                                if (p0 == getStartOffset() && p1 == getEndOffset()) {
                                    return this;
                                }
                                return createFragment(p0, p1);
                            }
                            return this;
                        }
                    };
                } else if (v instanceof ParagraphView) {
                    return new ParagraphView(e) {
                        protected SizeRequirements calculateMinorAxisRequirements(int axis, SizeRequirements r) {
                            if (r == null) {
                                r = new SizeRequirements();
                            }
                            float pref = layoutPool.getPreferredSpan(axis);
                            float min = layoutPool.getMinimumSpan(axis);
                            // Don't include insets, Box.getXXXSpan will include them.
                            r.minimum = (int) min;
                            r.preferred = Math.max(r.minimum, (int) pref);
                            r.maximum = Integer.MAX_VALUE;
                            r.alignment = 0.5f;
                            return r;
                        }

                    };
                }
                return v;
            }
        };
    }
}

And finally, the SmileyDocumentListener class :

public class SmileyDocumentListener implements DocumentListener {

    private JTextComponent owner;

    private HashMap<String,ImageIcon> smileMap;

    public SmileyDocumentListener(JTextComponent owner) {
        this.owner = owner;
        this.smileMap = new HashMap<String, ImageIcon>();

        this.smileMap.put(":)", new ImageIcon("resources/images/smileys/smile.gif"));
    }

    @Override
    public void insertUpdate(DocumentEvent event) {
        final DocumentEvent e = event;
        SwingUtilities.invokeLater(new Runnable() {
            public void run() {
                if (e.getDocument() instanceof StyledDocument) {
                    try {
                        StyledDocument doc=(StyledDocument)e.getDocument();
                        int start = Utilities.getRowStart(owner, Math.max(0, e.getOffset() - 1));
                        int end = Utilities.getWordStart(owner, e.getOffset() + e.getLength());
                        String text = doc.getText(start, end-start);

                        for (String token : smileMap.keySet()) {
                            int i = text.indexOf(token);
                            while (i >= 0) {
                                final SimpleAttributeSet attrs = new SimpleAttributeSet(doc.getCharacterElement(start + i).getAttributes());
                                if (StyleConstants.getIcon(attrs) == null) {
                                    StyleConstants.setIcon(attrs, smileMap.get(token));
                                    doc.remove(start + i, 2);
                                    doc.insertString(start + i, token, attrs);
                                }
                                i = text.indexOf(token, i+token.length());
                            }
                        }
                    } catch (BadLocationException e1) {
                        e1.printStackTrace();
                    }
                }
            }
        });
    }

    @Override
    public void removeUpdate(DocumentEvent e) {}

    @Override
    public void changedUpdate(DocumentEvent e) {}

}

Note : that I've tried changing r.alignment = 0.5f; to 0.0f in WrapHTMLEditorKit.java

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(2

屋顶上的小猫咪 2024-12-18 10:59:44

您可以尝试插入 HTML 标签“”。选择文本并应用一些字符属性(例如粗体)来创建单独的叶元素。然后用 IMG 标签替换元素的 HTML。

You can try to insert HTML tag "< img...>". Select the text and apply some character attribute (e.g. bold) to create a separate leaf Element. Then replace the element;s HTML with the IMG tag.

烟燃烟灭 2024-12-18 10:59:44

好吧,所以我不知道为什么图标在使用 text/html 内容类型的 JTextPane 中以这种方式显示(我尝试使用 JLabel 显示图标、直接设置 HTML 等。)底线是我认为 HTML 是必要的。

我替换

public class MessageDocument extends HTMLDocument {

public class MessageDocument extends StyledDocument {

我删除了 WrapHTMLEditorKit 并修改了初始化代码

messagePane = new JTextPane();
messagePane.setEditable(false);
messagePane.setEditorKit(new StyledEditorKit());
MessageDocument doc = new MessageDocument();
doc.addDocumentListener(new SmileyDocumentListener(messagePane));
messagePane.setStyledDocument(doc);

,使图标正常显示。

内容无需特殊类即可换行,我仍然可以使用 SimpleAttributeSet 对象。

Alright, so I don't know why the icons are shown that way in a JTextPane using a content type of text/html (and I tried using a JLabel displaying an icon, setting HTML directly, etc.) Bottom line is that I thought HTML was necessary.

I replaced

public class MessageDocument extends HTMLDocument {

by

public class MessageDocument extends StyledDocument {

I removed WrapHTMLEditorKit and modified the init code with

messagePane = new JTextPane();
messagePane.setEditable(false);
messagePane.setEditorKit(new StyledEditorKit());
MessageDocument doc = new MessageDocument();
doc.addDocumentListener(new SmileyDocumentListener(messagePane));
messagePane.setStyledDocument(doc);

and icons displayed normally.

The contents wraps without a special class and I can still use SimpleAttributeSet objects.

~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文