Java Swing实现的仿QQ气泡消息聊天窗口效果
2016-09-07 14:50
656 查看
下面讲讲聊天窗口的上半部分聊天消息显示区域的具体实现,尤其是气泡的绘制、图文的混排实现。
先讲讲气泡的实现思路。开始我想过很多气泡的实现方法,在研究了JTextPane的文档类及其内容插入删除排版后,我想利用JTextPane能够插入JComponent的特点,直接把一个JLabel或者JTextPane给插入进去,作为段落显示,同时对这个JLabel或者JTextPane进行自绘,形成圆角矩形边框,模拟气泡的效果。但是后来发现这种方法对于段落的实际格式控制,尤其是气泡大小随聊天窗口区域变化而改变的适应能力不行,且气泡的小箭头出不来,所以放弃了。后来在网上看到了一篇很早的帖子在讨论关于利用VC和MFC在RichEdit中模拟QQ聊天气泡效果,得到了一点启发。气泡的绘制并不是与段落插入在同一个层面上,气泡是直接在历史消息显示区域的JTextPane的自绘中去实现,也就是在JTextPane的paintComponent方法中去绘制,而段落文本、图片的插入还是在JTextPane的层面上用其Document对象去负责。这里就存在一个问题,JTextPane怎么知道气泡绘制的区域大小和位置坐标?我是通过一个支持多线程并发的消息队列来保存每条消息的段落显示区域的大小及其位置。这个队列在JTextPane的Document插入每条消息的时候,对消息的段落区域进行计算,得到其显示区域大小及位置坐标,然后保存在队列中,而JTextPane的paintComponent方法里则不断对这个消息队列进行迭代枚举得到每条消息的显示区域的大小及位置然后依此进行气泡的绘制。这个队列采用的是Java里的concurrentLinkedQueue,能够实现基本的多线程无锁队列读写操作,这个特性在后面还会用得到,而且非常重要。下面是历史消息显示区域的JTextPane子类的自绘实现,当然,这里的气泡绘制包含了小箭头效果的实现,同时能够支持左右两边不同头像方向情况下,气泡箭头方向也能够自适应调整:
先讲讲气泡的实现思路。开始我想过很多气泡的实现方法,在研究了JTextPane的文档类及其内容插入删除排版后,我想利用JTextPane能够插入JComponent的特点,直接把一个JLabel或者JTextPane给插入进去,作为段落显示,同时对这个JLabel或者JTextPane进行自绘,形成圆角矩形边框,模拟气泡的效果。但是后来发现这种方法对于段落的实际格式控制,尤其是气泡大小随聊天窗口区域变化而改变的适应能力不行,且气泡的小箭头出不来,所以放弃了。后来在网上看到了一篇很早的帖子在讨论关于利用VC和MFC在RichEdit中模拟QQ聊天气泡效果,得到了一点启发。气泡的绘制并不是与段落插入在同一个层面上,气泡是直接在历史消息显示区域的JTextPane的自绘中去实现,也就是在JTextPane的paintComponent方法中去绘制,而段落文本、图片的插入还是在JTextPane的层面上用其Document对象去负责。这里就存在一个问题,JTextPane怎么知道气泡绘制的区域大小和位置坐标?我是通过一个支持多线程并发的消息队列来保存每条消息的段落显示区域的大小及其位置。这个队列在JTextPane的Document插入每条消息的时候,对消息的段落区域进行计算,得到其显示区域大小及位置坐标,然后保存在队列中,而JTextPane的paintComponent方法里则不断对这个消息队列进行迭代枚举得到每条消息的显示区域的大小及位置然后依此进行气泡的绘制。这个队列采用的是Java里的concurrentLinkedQueue,能够实现基本的多线程无锁队列读写操作,这个特性在后面还会用得到,而且非常重要。下面是历史消息显示区域的JTextPane子类的自绘实现,当然,这里的气泡绘制包含了小箭头效果的实现,同时能够支持左右两边不同头像方向情况下,气泡箭头方向也能够自适应调整:
import java.awt.Color; import java.awt.Graphics; import java.awt.Graphics2D; import java.awt.Image; import java.awt.Point; import java.awt.RenderingHints; import java.util.Iterator; import java.util.concurrent.ConcurrentHashMap; import java.util.concurrent.ConcurrentLinkedQueue; /** * */ public class JIMHistoryTextPane extends JIMSendTextPane { private ConcurrentLinkedQueue<Message> messageConcurrentLinkedQueue; private ConcurrentHashMap<Integer, Image> senderHeadImageConcurrentHashMap; private Color otherMessageColor = new Color(188, 237, 245); private Color otherMessageBorderColor = new Color(156, 205, 213); private Color selfMessageColor = new Color(230, 230, 230); private Color selfMessageBorderColor = new Color(198, 198, 198); public JIMHistoryTextPane() { setEditable(false); // 用于显示历史消息,因此必须为只读模式,不允许用户修改内容 setOpaque(false); // 设置成背景透明后,完全自绘才会看到效果 } @Override public void paintComponent(Graphics g) { Graphics2D g2D = (Graphics2D) g; g2D.setRenderingHint(RenderingHints.KEY_ANTIALIASING, RenderingHints.VALUE_ANTIALIAS_ON); // 反锯齿平滑绘制 // 通过对并发消息链表遍历绘制全部消息气泡、消息发出者头像 if (messageConcurrentLinkedQueue != null) { Iterator<Message> iterator = messageConcurrentLinkedQueue.iterator(); while (iterator.hasNext()) { Message message = iterator.next(); Point point = message.getMessagePaintLeftTop(); if (point != null) { // 绘制消息发出者头像 if (senderHeadImageConcurrentHashMap != null) { Image image = senderHeadImageConcurrentHashMap.get(message.getSenderHeadImageID()); if (image != null) { if (message.isSelfSender()) { g2D.drawImage(image, this.getWidth() - image.getWidth(null) - 9, point.y - 25, null); } else { // 消息发出者是别人,则头像靠左显示 g2D.drawImage(image, 9, point.y - 25, null); } } } // 绘制额消息气泡左边小箭头 int xPoints[] = new int[3]; int yPoints[] = new int[3]; if (message.isSelfSender()) { // 绘制自己消息圆角消息气泡矩形 g2D.setColor(selfMessageColor); g2D.fillRoundRect(point.x - 7, point.y - 7, message.getMessagePaintWidth() + 14, message.getMessagePaintHeight() + 14, 10, 10); // 绘制圆角消息气泡边框 g2D.setColor(selfMessageBorderColor); g2D.drawRoundRect(point.x - 7, point.y - 7, message.getMessagePaintWidth() + 14, message.getMessagePaintHeight() + 14, 10, 10); // 消息发出者是自己,则头像靠右显示 xPoints[0] = (point.x - 7) + (message.getMessagePaintWidth() + 14); yPoints[0] = point.y; xPoints[1] = xPoints[0] + 7; yPoints[1] = point.y; xPoints[2] = xPoints[0]; yPoints[2] = point.y + 7; g2D.setColor(selfMessageColor); g2D.fillPolygon(xPoints, yPoints, 3); g2D.setColor(selfMessageBorderColor); g2D.drawPolyline(xPoints, yPoints, 3); g2D.setColor(selfMessageColor); g2D.drawLine(xPoints[0], yPoints[0] + 1, xPoints[2], yPoints[2] - 1); } else { // 绘制别人消息圆角消息气泡矩形 // 绘制圆角消息气泡矩形 g2D.setColor(otherMessageColor); g2D.fillRoundRect(point.x - 7, point.y - 7, message.getMessagePaintWidth() + 14, message.getMessagePaintHeight() + 14, 10, 10); // 绘制圆角消息气泡边框 g2D.setColor(otherMessageBorderColor); g2D.drawRoundRect(point.x - 7, point.y - 7, message.getMessagePaintWidth() + 14, message.getMessagePaintHeight() + 14, 10, 10); // 消息发出者是别人,则头像靠左显示 xPoints[0] = point.x - 7; yPoints[0] = point.y; xPoints[1] = xPoints[0] - 7; yPoints[1] = point.y; xPoints[2] = xPoints[0]; yPoints[2] = point.y + 7; g2D.setColor(otherMessageColor); g2D.fillPolygon(xPoints, yPoints, 3); g2D.setColor(otherMessageBorderColor); g2D.drawPolyline(xPoints, yPoints, 3); g2D.setColor(otherMessageColor); g2D.drawLine(xPoints[0], yPoints[0] + 1, xPoints[2], yPoints[2] - 1); } } } // while } super.paintComponent(g); // 执行默认组件绘制(消息文本、图片以及段落显示等内容) } public void setMessageConcurrentLinkedQueue(ConcurrentLinkedQueue<Message> messageConcurrentLinkedQueue) { this.messageConcurrentLinkedQueue = messageConcurrentLinkedQueue; } public void setSenderHeadImageConcurrentHashMap(ConcurrentHashMap<Integer, Image> senderHeadImageConcurrentHashMap) { this.senderHeadImageConcurrentHashMap = senderHeadImageConcurrentHashMap; } }
相关文章推荐
- WindowLess RichEdit 实现QQ聊天窗口的气泡效果,设计思路和方法。
- Android使用贝塞尔线高仿QQ聊天消息气泡的拖拽效果
- 实现QQ聊天气泡效果
- 【HTML5】实现QQ聊天气泡效果
- 【HTML5】实现QQ聊天气泡效果
- BezierDemo源码解析-实现qq消息气泡拖拽消失的效果
- Qt+html+JavaScript实现类似QQ聊天界面的气泡效果
- 使用css实现QQ聊天气泡效果
- QQ收到消息窗口闪烁效果实现[C#]
- html5 实现qq聊天的气泡效果
- 贝塞尔曲线实现QQ未读消息气泡拖拽效果
- 用border-image实现QQ气泡聊天窗效果
- 如何实现qq的那种有消息过来,窗口就在任务栏闪的效果?
- 怎么用Java实现QQ消息输入框这种能表情图片和文字混排的效果
- 【HTML5】简单实现QQ聊天气泡效果
- 安卓仿手机QQ消息BadgeView气泡跟随手指移动,并实现进出动画效果。
- 使用swiper插件实现qq聊天窗口按钮滑动效果
- C#+ html 实现类似QQ聊天界面的气泡效果
- WinForm:实现类似QQ消息框一样的右下角消息提示窗口
- 实现MSN,QQ消息提示效果[ASP.Net]