您的位置:首页 > 编程语言 > Java开发

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子类的自绘实现,当然,这里的气泡绘制包含了小箭头效果的实现,同时能够支持左右两边不同头像方向情况下,气泡箭头方向也能够自适应调整: 
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;
}
}
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: