Java Swing的JTextPane支持格式化文本,即字体、大小、加粗、倾斜、下划线、颜色等,还支持图片、任意组件Component及段落格式等,相关概念还有文档StyledDocument、编辑器StyledEditorKit、格式集AttributeSet和格式工具StyleConstants。
常见的聊天窗口之记录框和输入框的显示不太一样,记录框每次都要添加格式化文本,这时需要用到文档模型的插入文本接口:JTextPane.getDocument().insert(int position, String content, AttributeSet attributes),位置通常可取JTextPane.getDocument().getLength(),格式集可以先new SimpleAttributeSet(),然后用StyleConstants.setFontFamily(attributes,family)等配置格式,这样格式化文本就可以正常添加了。记录框一般会有两个或多个人的消息,可以将各人的格式分别保存起来,JTextPane.getStyledDocument().add(String userid, AttributeSet attributes),收到消息时检查是否有格式信息,有的话取出他的格式并适当修改即可,JTextPane.etStyleDocument().getStyle(String userid),插入文本时可直接从文档获取发送者的格式,如果没有则提供一个默认值。
输入框不太一样,修改格式时所有文本都要跟者改变以预览效果,同时输入框还有输入格式的概念,即格式配置好后再输入内容都是以预定的格式,获得输入格式可用JTextPane.getInputAttributes(),用StyleConstants配置它后就可以影响之后输入文字的格式了。如果修改了格式,输入框已有的文字需要修改格式,可以用JTextPane.getStyledDocument().setCharacterAttributes(int offset, int length, Attributes attributes, boolean replace),由于要修改所有输入框文本,所以offset可取0,length可取JTextPane.getDocument().getLength(),格式集是重新配置的,replace可取false,即不清空原有的格式。如果只是修改所选文本的格式,用JTextPane.setCharacterAttributes(Attributes attributes, boolean replace)就够了。
格式集的配置用StyleContants,字体setFontFamily,大小setFontSize,颜色setForeground,加粗setBold,倾斜setItalics,下划线setUnderlined等,获得字体名可用GraphicsEnvironment.getLocalGraphicsEnvironment().getAvailableFontFamilyNames(),字体包含名字、大小、样式(加粗、倾斜)三要素。如果格式改变了,就把它绑定到即将要发的消息中,对方就可以识别格式并正确显示了。
表情属于图片,加入输入框时需要使用文本插入的接口,JTextPane.getDocument().insert(int position, String emoticon, Attributes attributes),其格式集可为new SimpleAttibuteSet(),StyleConstants.setIcon(attributes, icon),这样显示时就是表情图片了,通过JTextPane.getText()仍能获得全部文本,两个相同表情连续出现时可能只显示一个,这时可以简单插入空格。
记录框显示时需要分析文本,因为表情、超链接等在下层都是纯文本,表情查找可用String.indexOf,超链接检索可用Pattern.compile("(https?|ftp|file)://[-a-zA-Z0-9+&@#/%?=~_|!:,.;]*[-a-zA-Z0-9+&@#/%=~_|]"),这样就能将各类文本以正确的格式添加了。超链接的格式可以自己配,比如12号+蓝色+下划线等,要响应超链接点击可监听mouseClicked事件,定位鼠标位置JTextComponent.viewToModel(e.getPoint()),获得文档元素Element = JTextPane.getStyledDocument().getCharacterElement(pos),获得链接地址(String)element.getAttributes().getAttribute("link"),先插入链接时保存了地址到格式AttributeSet.addAttibute("link", address),最后打开链接Desktop.getDesktop().browse(URL url)。
表情的删除需要对AbstractDocument设置特殊的DocumentFilter,在remove方法中判断当前位置offset处的元素Element=StyledDocument.getCharacterElement(pos)是否表情元素,比如获取其格式集AttributeSet=Element.getAttributes(),提取其icon属性是否为空StyleConstants.getIcon(as)!=null,如果不空则从位置element.getStartOffset()删除element.getEndOffset()-element.getStartOffset()个字符即可(需要遍历offset+length之内的字符,如果有表情则拓宽删除范围),否则交给父类默认处理。
效果图: