具有自定义文档和编辑器的 JTextPane 无法正确显示图标
我们正在用 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 技术交流群。
绑定邮箱获取回复消息
由于您还没有绑定你的真实邮箱,如果其他用户或者作者回复了您的评论,将不能在第一时间通知您!
发布评论
评论(2)
您可以尝试插入 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.
好吧,所以我不知道为什么图标在使用
text/html
内容类型的JTextPane
中以这种方式显示(我尝试使用JLabel
显示图标、直接设置 HTML 等。)底线是我认为 HTML 是必要的。我替换
为
我删除了
WrapHTMLEditorKit
并修改了初始化代码,使图标正常显示。
内容无需特殊类即可换行,我仍然可以使用
SimpleAttributeSet
对象。Alright, so I don't know why the icons are shown that way in a
JTextPane
using a content type oftext/html
(and I tried using aJLabel
displaying an icon, setting HTML directly, etc.) Bottom line is that I thought HTML was necessary.I replaced
by
I removed
WrapHTMLEditorKit
and modified the init code withand icons displayed normally.
The contents wraps without a special class and I can still use
SimpleAttributeSet
objects.