1、简介
由于最近的项目需要,做了些相关IM的工作。所以聊天框也是必不可少的一部分。聊天框的制作分很多种,本文以QListWidget+QPainter绘制的Item做了一个Demo。该Demo只是做一个示例,代码已公布如下,需要的拿去!
2、效果图
3、实现原理
气泡式聊天的显示是由QListWidget作为控件,每个气泡是由QListWidgetItem提升成QWidget来实现的。每个气泡可以理解位是一个QWidget,这样可以自由布置QWidget里面的内容。每个Item保存聊天的对话、发送状态、时间、种类等。
这个QWidget主要是显示一个头像+气泡,气泡里面是聊天的内容等。
气泡是在paintEvent事件中,采用QPainter来绘制的。
4、核心代码
4.1、头文件
#ifndef QNCHATMESSAGE_H
#define QNCHATMESSAGE_H
#include
class QPaintEvent;
class QPainter;
class QLabel;
class QMovie;
class QNChatMessage : public QWidget
{
Q_OBJECT
public:
explicit QNChatMessage(QWidget *parent = nullptr);
enum User_Type{
User_System,//系统
User_Me, //自己
User_She, //用户
User_Time, //时间
};
void setTextSuccess();
void setText(QString text, QString time, QSize allSize, User_Type userType);
QSize getRealString(QString src);
QSize fontRect(QString str);
inline QString text() {return m_msg;}
inline QString time() {return m_time;}
inline User_Type userType() {return m_userType;}
protected:
void paintEvent(QPaintEvent *event);
private:
QString m_msg;
QString m_time;
QString m_curTime;
QSize m_allSize;
User_Type m_userType = User_System;
int m_kuangWidth;
int m_textWidth;
int m_spaceWid;
int m_lineHeight;
QRect m_iconLeftRect;
QRect m_iconRightRect;
QRect m_sanjiaoLeftRect;
QRect m_sanjiaoRightRect;
QRect m_kuangLeftRect;
QRect m_kuangRightRect;
QRect m_textLeftRect;
QRect m_textRightRect;
QPixmap m_leftPixmap;
QPixmap m_rightPixmap;
QLabel* m_loading = Q_NULLPTR;
QMovie* m_loadingMovie = Q_NULLPTR;
bool m_isSending = false;
};
#endif // QNCHATMESSAGE_H
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
- 11
- 12
- 13
- 14
- 15
- 16
- 17
- 18
- 19
- 20
- 21
- 22
- 23
- 24
- 25
- 26
- 27
- 28
- 29
- 30
- 31
- 32
- 33
- 34
- 35
- 36
- 37
- 38
- 39
- 40
- 41
- 42
- 43
- 44
- 45
- 46
- 47
- 48
- 49
- 50
- 51
- 52
- 53
- 54
- 55
- 56
- 57
- 58
- 59
- 60
- 61
- 62
- 63
4.2、源文件
#include "qnchatmessage.h"
#include
#include
#include
#include
#include
#include
#include
QNChatMessage::QNChatMessage(QWidget *parent) : QWidget(parent)
{
QFont te_font = this->font();
te_font.setFamily("MicrosoftYaHei");
te_font.setPointSize(12);
// te_font.setWordSpacing(0);
// te_font.setLetterSpacing(QFont::PercentageSpacing,0);
// te_font.setLetterSpacing(QFont::PercentageSpacing, 100); //300%,100为默认 //设置字间距%
// te_font.setLetterSpacing(QFont::AbsoluteSpacing, 0); //设置字间距为3像素 //设置字间距像素值
this->setFont(te_font);
m_leftPixmap = QPixmap(":/img/Customer Copy.png");
m_rightPixmap = QPixmap(":/img/CustomerService.png");
m_loadingMovie = new QMovie(this);
m_loadingMovie->setFileName(":/img/loading4.gif");
m_loading = new QLabel(this);
m_loading->setMovie(m_loadingMovie);
m_loading->resize(16,16);
m_loading->setAttribute(Qt::WA_TranslucentBackground , true);
m_loading->setAutoFillBackground(false);
}
void QNChatMessage::setTextSuccess()
{
m_loading->hide();
m_loadingMovie->stop();
m_isSending = true;
}
void QNChatMessage::setText(QString text, QString time, QSize allSize, QNChatMessage::User_Type userType)
{
m_msg = text;
m_userType = userType;
m_time = time;
m_curTime = QDateTime::fromTime_t(time.toInt()).toString("hh:mm");
m_allSize = allSize;
if(userType == User_Me) {
if(!m_isSending) {
m_loading->move(m_kuangRightRect.x() - m_loading->width() - 10, m_kuangRightRect.y()+m_kuangRightRect.height()/2- m_loading->height()/2);
m_loading->show();
m_loadingMovie->start();
}
} else {
m_loading->hide();
}
this->update();
}
QSize QNChatMessage::fontRect(QString str)
{
m_msg = str;
int minHei = 30;
int iconWH = 40;
int iconSpaceW = 20;
int iconRectW = 5;
int iconTMPH = 10;
int sanJiaoW = 6;
int kuangTMP = 20;
int textSpaceRect = 12;
m_kuangWidth = this->width() - kuangTMP - 2*(iconWH+iconSpaceW+iconRectW);
m_textWidth = m_kuangWidth - 2*textSpaceRect;
m_spaceWid = this->width() - m_textWidth;
m_iconLeftRect = QRect(iconSpaceW, iconTMPH, iconWH, iconWH);
m_iconRightRect = QRect(this->width() - iconSpaceW - iconWH, iconTMPH, iconWH, iconWH);
QSize size = getRealString(m_msg); // 整个的size
qDebug() << "fontRect Size:" << size;
int hei = size.height() < minHei ? minHei : size.height();
m_sanjiaoLeftRect = QRect(iconWH+iconSpaceW+iconRectW, m_lineHeight/2, sanJiaoW, hei - m_lineHeight);
m_sanjiaoRightRect = QRect(this->width() - iconRectW - iconWH - iconSpaceW - sanJiaoW, m_lineHeight/2, sanJiaoW, hei - m_lineHeight);
if(size.width() < (m_textWidth+m_spaceWid)) {
m_kuangLeftRect.setRect(m_sanjiaoLeftRect.x()+m_sanjiaoLeftRect.width(), m_lineHeight/4*3, size.width()-m_spaceWid+2*textSpaceRect, hei-m_lineHeight);
m_kuangRightRect.setRect(this->width() - size.width() + m_spaceWid - 2*textSpaceRect - iconWH - iconSpaceW - iconRectW - sanJiaoW,
m_lineHeight/4*3, size.width()-m_spaceWid+2*textSpaceRect, hei-m_lineHeight);
} else {
m_kuangLeftRect.setRect(m_sanjiaoLeftRect.x()+m_sanjiaoLeftRect.width(), m_lineHeight/4*3, m_kuangWidth, hei-m_lineHeight);
m_kuangRightRect.setRect(iconWH + kuangTMP + iconSpaceW + iconRectW - sanJiaoW, m_lineHeight/4*3, m_kuangWidth, hei-m_lineHeight);
}
m_textLeftRect.setRect(m_kuangLeftRect.x()+textSpaceRect,m_kuangLeftRect.y()+iconTMPH,
m_kuangLeftRect.width()-2*textSpaceRect,m_kuangLeftRect.height()-2*iconTMPH);
m_textRightRect.setRect(m_kuangRightRect.x()+textSpaceRect,m_kuangRightRect.y()+iconTMPH,
m_kuangRightRect.width()-2*textSpaceRect,m_kuangRightRect.height()-2*iconTMPH);
return QSize(size.width(), hei);
}
QSize QNChatMessage::getRealString(QString src)
{
QFontMetricsF fm(this->font());
m_lineHeight = fm.lineSpacing();
int nCount = src.count("\n");
int nMaxWidth = 0;
if(nCount == 0) {
nMaxWidth = fm.width(src);
QString value = src;
if(nMaxWidth > m_textWidth) {
nMaxWidth = m_textWidth;
int size = m_textWidth / fm.width(" ");
int num = fm.width(value) / m_textWidth;
int ttmp = num*fm.width(" ");
num = ( fm.width(value) ) / m_textWidth;
nCount += num;
QString temp = "";
for(int i = 0; i < num; i++) {
temp += value.mid(i*size, (i+1)*size) + "\n";
}
src.replace(value, temp);
}
} else {
for(int i = 0; i < (nCount + 1); i++) {
QString value = src.split("\n").at(i);
nMaxWidth = fm.width(value) > nMaxWidth ? fm.width(value) : nMaxWidth;
if(fm.width(value) > m_textWidth) {
nMaxWidth = m_textWidth;
int size = m_textWidth / fm.width(" ");
int num = fm.width(value) / m_textWidth;
num = ((i+num)*fm.width(" ") + fm.width(value)) / m_textWidth;
nCount += num;
QString temp = "";
for(int i = 0; i < num; i++) {
temp += value.mid(i*size, (i+1)*size) + "\n";
}
src.replace(value, temp);
}
}
}
return QSize(nMaxWidth+m_spaceWid, (nCount + 1) * m_lineHeight+2*m_lineHeight);
}
void QNChatMessage::paintEvent(QPaintEvent *event)
{
Q_UNUSED(event);
QPainter painter(this);
painter.setRenderHints(QPainter::Antialiasing | QPainter::SmoothPixmapTransform);//消锯齿
painter.setPen(Qt::NoPen);
painter.setBrush(QBrush(Qt::gray));
if(m_userType == User_Type::User_She) { // 用户
//头像
// painter.drawRoundedRect(m_iconLeftRect,m_iconLeftRect.width(),m_iconLeftRect.height());
painter.drawPixmap(m_iconLeftRect, m_leftPixmap);
//框加边
QColor col_KuangB(234, 234, 234);
painter.setBrush(QBrush(col_KuangB));
painter.drawRoundedRect(m_kuangLeftRect.x()-1,m_kuangLeftRect.y()-1,m_kuangLeftRect.width()+2,m_kuangLeftRect.height()+2,4,4);
//框
QColor col_Kuang(255,255,255);
painter.setBrush(QBrush(col_Kuang));
painter.drawRoundedRect(m_kuangLeftRect,4,4);
//三角
QPointF points[3] = {
QPointF(m_sanjiaoLeftRect.x(), 30),
QPointF(m_sanjiaoLeftRect.x()+m_sanjiaoLeftRect.width(), 25),
QPointF(m_sanjiaoLeftRect.x()+m_sanjiaoLeftRect.width(), 35),
};
QPen pen;
pen.setColor(col_Kuang);
painter.setPen(pen);
painter.drawPolygon(points, 3);
//三角加边
QPen penSanJiaoBian;
penSanJiaoBian.setColor(col_KuangB);
painter.setPen(penSanJiaoBian);
painter.drawLine(QPointF(m_sanjiaoLeftRect.x() - 1, 30), QPointF(m_sanjiaoLeftRect.x()+m_sanjiaoLeftRect.width(), 24));
painter.drawLine(QPointF(m_sanjiaoLeftRect.x() - 1, 30), QPointF(m_sanjiaoLeftRect.x()+m_sanjiaoLeftRect.width(), 36));
//内容
QPen penText;
penText.setColor(QColor(51,51,51));
painter.setPen(penText);
QTextOption option(Qt::AlignLeft | Qt::AlignVCenter);
option.setWrapMode(QTextOption::WrapAtWordBoundaryOrAnywhere);
painter.setFont(this->font());
painter.drawText(m_textLeftRect, m_msg,option);
} else if(m_userType == User_Type::User_Me) { // 自己
//头像
// painter.drawRoundedRect(m_iconRightRect,m_iconRightRect.width(),m_iconRightRect.height());
painter.drawPixmap(m_iconRightRect, m_rightPixmap);
//框
QColor col_Kuang(75,164,242);
painter.setBrush(QBrush(col_Kuang));
painter.drawRoundedRect(m_kuangRightRect,4,4);
//三角
QPointF points[3] = {
QPointF(m_sanjiaoRightRect.x()+m_sanjiaoRightRect.width(), 30),
QPointF(m_sanjiaoRightRect.x(), 25),
QPointF(m_sanjiaoRightRect.x(), 35),
};
QPen pen;
pen.setColor(col_Kuang);
painter.setPen(pen);
painter.drawPolygon(points, 3);
//内容
QPen penText;
penText.setColor(Qt::white);
painter.setPen(penText);
QTextOption option(Qt::AlignLeft | Qt::AlignVCenter);
option.setWrapMode(QTextOption::WrapAtWordBoundaryOrAnywhere);
painter.setFont(this->font());
painter.drawText(m_textRightRect,m_msg,option);
} else if(m_userType == User_Type::User_Time) { // 时间
QPen penText;
penText.setColor(QColor(153,153,153));
painter.setPen(penText);
QTextOption option(Qt::AlignCenter);
option.setWrapMode(QTextOption::WrapAtWordBoundaryOrAnywhere);
QFont te_font = this->font();
te_font.setFamily("MicrosoftYaHei");
te_font.setPointSize(10);
painter.setFont(te_font);
painter.drawText(this->rect(),m_curTime,option);
}
}
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
- 11
- 12
- 13
- 14
- 15
- 16
- 17
- 18
- 19
- 20
- 21
- 22
- 23
- 24
- 25
- 26
- 27
- 28
- 29
- 30
- 31
- 32
- 33
- 34
- 35
- 36
- 37
- 38
- 39
- 40
- 41
- 42
- 43
- 44
- 45
- 46
- 47
- 48
- 49
- 50
- 51
- 52
- 53
- 54
- 55
- 56
- 57
- 58
- 59
- 60
- 61
- 62
- 63
- 64
- 65
- 66
- 67
- 68
- 69
- 70
- 71
- 72
- 73
- 74
- 75
- 76
- 77
- 78
- 79
- 80
- 81
- 82
- 83
- 84
- 85
- 86
- 87
- 88
- 89
- 90
- 91
- 92
- 93
- 94
- 95
- 96
- 97
- 98
- 99
- 100
- 101
- 102
- 103
- 104
- 105
- 106
- 107
- 108
- 109
- 110
- 111
- 112
- 113
- 114
- 115
- 116
- 117
- 118
- 119
- 120
- 121
- 122
- 123
- 124
- 125
- 126
- 127
- 128
- 129
- 130
- 131
- 132
- 133
- 134
- 135
- 136
- 137
- 138
- 139
- 140
- 141
- 142
- 143
- 144
- 145
- 146
- 147
- 148
- 149
- 150
- 151
- 152
- 153
- 154
- 155
- 156
- 157
- 158
- 159
- 160
- 161
- 162
- 163
- 164
- 165
- 166
- 167
- 168
- 169
- 170
- 171
- 172
- 173
- 174
- 175
- 176
- 177
- 178
- 179
- 180
- 181
- 182
- 183
- 184
- 185
- 186
- 187
- 188
- 189
- 190
- 191
- 192
- 193
- 194
- 195
- 196
- 197
- 198
- 199
- 200
- 201
- 202
- 203
- 204
- 205
- 206
- 207
- 208
- 209
- 210
- 211
- 212
- 213
- 214
- 215
- 216
- 217
- 218
- 219
- 220
- 221
- 222
- 223
- 224
- 225
- 226
- 227
- 228
- 229
- 230
- 231
- 232
- 233
- 234
5、代码分享
5.1、Github
地址:https://github.com/ShaShiDiZhuanLan/Demo_MessageChat_Qt
5.2、码云
地址:https://gitee.com/ShaShiDiZhuanLan/Demo_MessageChat
评论记录:
回复评论: