├── .gitignore ├── Demo_MessageChat_Qt.pro ├── README.md ├── chatmessage ├── qnchatmessage.cpp └── qnchatmessage.h ├── img.qrc ├── img ├── Customer Copy.png ├── CustomerService.png └── loading4.gif ├── main.cpp ├── mainwindow.cpp ├── mainwindow.h ├── mainwindow.ui ├── 效果1.png └── 效果2.png /.gitignore: -------------------------------------------------------------------------------- 1 | # C++ objects and libs 2 | *.slo 3 | *.lo 4 | *.o 5 | *.a 6 | *.la 7 | *.lai 8 | *.so 9 | *.so.* 10 | *.dll 11 | *.dylib 12 | 13 | # Qt-es 14 | object_script.*.Release 15 | object_script.*.Debug 16 | *_plugin_import.cpp 17 | /.qmake.cache 18 | /.qmake.stash 19 | *.pro.user 20 | *.pro.user.* 21 | *.qbs.user 22 | *.qbs.user.* 23 | *.moc 24 | moc_*.cpp 25 | moc_*.h 26 | qrc_*.cpp 27 | ui_*.h 28 | *.qmlc 29 | *.jsc 30 | Makefile* 31 | *build-* 32 | *.qm 33 | *.prl 34 | 35 | # Qt unit tests 36 | target_wrapper.* 37 | 38 | # QtCreator 39 | *.autosave 40 | 41 | # QtCreator Qml 42 | *.qmlproject.user 43 | *.qmlproject.user.* 44 | 45 | # QtCreator CMake 46 | CMakeLists.txt.user* 47 | 48 | # QtCreator 4.8< compilation database 49 | compile_commands.json 50 | 51 | # QtCreator local machine specific files for imported projects 52 | *creator.user* 53 | -------------------------------------------------------------------------------- /Demo_MessageChat_Qt.pro: -------------------------------------------------------------------------------- 1 | #------------------------------------------------- 2 | # 3 | # Project created by QtCreator 2018-07-23 沙振宇 4 | # 5 | #------------------------------------------------- 6 | QT += core gui 7 | 8 | greaterThan(QT_MAJOR_VERSION, 4): QT += widgets 9 | 10 | TARGET = Demo_MessageChat 11 | TEMPLATE = app 12 | 13 | 14 | SOURCES += main.cpp\ 15 | mainwindow.cpp \ 16 | chatmessage/qnchatmessage.cpp 17 | 18 | HEADERS += mainwindow.h \ 19 | chatmessage/qnchatmessage.h 20 | 21 | FORMS += mainwindow.ui 22 | 23 | RESOURCES += \ 24 | img.qrc 25 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # 1、Qt的应用开发 之 Demo_MessageChat_Qt 2 | Qt5气泡式聊天框——QListWidget+QPainter。
3 | 气泡式聊天的显示是由QListWidget作为控件,每个气泡是由QListWidgetItem提升成QWidget来实现的。每个气泡可以理解为可以自由布置里面内容的QWidget。每个Item保存聊天的对话、发送状态、时间、种类等。这个QWidget主要是显示一个头像+气泡,气泡里面是聊天的内容等。气泡是在paintEvent事件中,采用QPainter来绘制的。
4 |
5 | # 2、更新信息 6 | 开发者:沙振宇(沙师弟专栏)
7 | 创建时间:2018-07-23
8 | 最后一次更新时间:2019-12-27
9 | 此项目CSDN博客:Qt5气泡式聊天框——QListWidget+QPainter实现
10 | 此项目博客地址:https://shazhenyu.blog.csdn.net/article/details/81505832
11 |
12 | # 3、效果 13 | ![image](https://github.com/ShaShiDiZhuanLan/Demo_MessageChat_Qt/blob/master/%E6%95%88%E6%9E%9C1.png) 14 | ![image](https://github.com/ShaShiDiZhuanLan/Demo_MessageChat_Qt/blob/master/%E6%95%88%E6%9E%9C2.png) -------------------------------------------------------------------------------- /chatmessage/qnchatmessage.cpp: -------------------------------------------------------------------------------- 1 | /*------------------------------------------------- 2 | # 3 | # Project created by QtCreator 4 | # Author: 沙振宇 5 | # CreateTime: 2018-07-23 6 | # UpdateTime: 2019-12-27 7 | # Info: Qt5气泡式聊天框——QListWidget+QPainter实现 8 | # Url:https://shazhenyu.blog.csdn.net/article/details/81505832 9 | # Github:https://github.com/ShaShiDiZhuanLan/Demo_MessageChat_Qt 10 | # 11 | #-------------------------------------------------*/ 12 | #include "qnchatmessage.h" 13 | #include 14 | #include 15 | #include 16 | #include 17 | #include 18 | #include 19 | #include 20 | 21 | QNChatMessage::QNChatMessage(QWidget *parent) : QWidget(parent) 22 | { 23 | QFont te_font = this->font(); 24 | te_font.setFamily("MicrosoftYaHei"); 25 | te_font.setPointSize(12); 26 | // te_font.setWordSpacing(0); 27 | // te_font.setLetterSpacing(QFont::PercentageSpacing,0); 28 | // te_font.setLetterSpacing(QFont::PercentageSpacing, 100); //300%,100为默认 //设置字间距% 29 | // te_font.setLetterSpacing(QFont::AbsoluteSpacing, 0); //设置字间距为3像素 //设置字间距像素值 30 | this->setFont(te_font); 31 | m_leftPixmap = QPixmap(":/img/Customer Copy.png"); 32 | m_rightPixmap = QPixmap(":/img/CustomerService.png"); 33 | 34 | m_loadingMovie = new QMovie(this); 35 | m_loadingMovie->setFileName(":/img/loading4.gif"); 36 | m_loading = new QLabel(this); 37 | m_loading->setMovie(m_loadingMovie); 38 | m_loading->resize(16,16); 39 | m_loading->setAttribute(Qt::WA_TranslucentBackground , true); 40 | m_loading->setAutoFillBackground(false); 41 | } 42 | 43 | void QNChatMessage::setTextSuccess() 44 | { 45 | m_loading->hide(); 46 | m_loadingMovie->stop(); 47 | m_isSending = true; 48 | } 49 | 50 | void QNChatMessage::setText(QString text, QString time, QSize allSize, QNChatMessage::User_Type userType) 51 | { 52 | m_msg = text; 53 | m_userType = userType; 54 | m_time = time; 55 | m_curTime = QDateTime::fromTime_t(time.toInt()).toString("hh:mm"); 56 | m_allSize = allSize; 57 | if(userType == User_Me) { 58 | if(!m_isSending) { 59 | m_loading->move(m_kuangRightRect.x() - m_loading->width() - 10, m_kuangRightRect.y()+m_kuangRightRect.height()/2- m_loading->height()/2); 60 | m_loading->show(); 61 | m_loadingMovie->start(); 62 | } 63 | } else { 64 | m_loading->hide(); 65 | } 66 | 67 | this->update(); 68 | } 69 | 70 | QSize QNChatMessage::fontRect(QString str) 71 | { 72 | m_msg = str; 73 | int minHei = 30; 74 | int iconWH = 40; 75 | int iconSpaceW = 20; 76 | int iconRectW = 5; 77 | int iconTMPH = 10; 78 | int sanJiaoW = 6; 79 | int kuangTMP = 20; 80 | int textSpaceRect = 12; 81 | m_kuangWidth = this->width() - kuangTMP - 2*(iconWH+iconSpaceW+iconRectW); 82 | m_textWidth = m_kuangWidth - 2*textSpaceRect; 83 | m_spaceWid = this->width() - m_textWidth; 84 | m_iconLeftRect = QRect(iconSpaceW, iconTMPH, iconWH, iconWH); 85 | m_iconRightRect = QRect(this->width() - iconSpaceW - iconWH, iconTMPH, iconWH, iconWH); 86 | 87 | QSize size = getRealString(m_msg); // 整个的size 88 | 89 | qDebug() << "fontRect Size:" << size; 90 | int hei = size.height() < minHei ? minHei : size.height(); 91 | 92 | m_sanjiaoLeftRect = QRect(iconWH+iconSpaceW+iconRectW, m_lineHeight/2, sanJiaoW, hei - m_lineHeight); 93 | m_sanjiaoRightRect = QRect(this->width() - iconRectW - iconWH - iconSpaceW - sanJiaoW, m_lineHeight/2, sanJiaoW, hei - m_lineHeight); 94 | 95 | if(size.width() < (m_textWidth+m_spaceWid)) { 96 | m_kuangLeftRect.setRect(m_sanjiaoLeftRect.x()+m_sanjiaoLeftRect.width(), m_lineHeight/4*3, size.width()-m_spaceWid+2*textSpaceRect, hei-m_lineHeight); 97 | m_kuangRightRect.setRect(this->width() - size.width() + m_spaceWid - 2*textSpaceRect - iconWH - iconSpaceW - iconRectW - sanJiaoW, 98 | m_lineHeight/4*3, size.width()-m_spaceWid+2*textSpaceRect, hei-m_lineHeight); 99 | } else { 100 | m_kuangLeftRect.setRect(m_sanjiaoLeftRect.x()+m_sanjiaoLeftRect.width(), m_lineHeight/4*3, m_kuangWidth, hei-m_lineHeight); 101 | m_kuangRightRect.setRect(iconWH + kuangTMP + iconSpaceW + iconRectW - sanJiaoW, m_lineHeight/4*3, m_kuangWidth, hei-m_lineHeight); 102 | } 103 | m_textLeftRect.setRect(m_kuangLeftRect.x()+textSpaceRect,m_kuangLeftRect.y()+iconTMPH, 104 | m_kuangLeftRect.width()-2*textSpaceRect,m_kuangLeftRect.height()-2*iconTMPH); 105 | m_textRightRect.setRect(m_kuangRightRect.x()+textSpaceRect,m_kuangRightRect.y()+iconTMPH, 106 | m_kuangRightRect.width()-2*textSpaceRect,m_kuangRightRect.height()-2*iconTMPH); 107 | 108 | return QSize(size.width(), hei); 109 | } 110 | 111 | QSize QNChatMessage::getRealString(QString src) 112 | { 113 | QFontMetricsF fm(this->font()); 114 | m_lineHeight = fm.lineSpacing(); 115 | int nCount = src.count("\n"); 116 | int nMaxWidth = 0; 117 | if(nCount == 0) { 118 | nMaxWidth = fm.width(src); 119 | QString value = src; 120 | if(nMaxWidth > m_textWidth) { 121 | nMaxWidth = m_textWidth; 122 | int size = m_textWidth / fm.width(" "); 123 | int num = fm.width(value) / m_textWidth; 124 | int ttmp = num*fm.width(" "); 125 | num = ( fm.width(value) ) / m_textWidth; 126 | nCount += num; 127 | QString temp = ""; 128 | for(int i = 0; i < num; i++) { 129 | temp += value.mid(i*size, (i+1)*size) + "\n"; 130 | } 131 | src.replace(value, temp); 132 | } 133 | } else { 134 | for(int i = 0; i < (nCount + 1); i++) { 135 | QString value = src.split("\n").at(i); 136 | nMaxWidth = fm.width(value) > nMaxWidth ? fm.width(value) : nMaxWidth; 137 | if(fm.width(value) > m_textWidth) { 138 | nMaxWidth = m_textWidth; 139 | int size = m_textWidth / fm.width(" "); 140 | int num = fm.width(value) / m_textWidth; 141 | num = ((i+num)*fm.width(" ") + fm.width(value)) / m_textWidth; 142 | nCount += num; 143 | QString temp = ""; 144 | for(int i = 0; i < num; i++) { 145 | temp += value.mid(i*size, (i+1)*size) + "\n"; 146 | } 147 | src.replace(value, temp); 148 | } 149 | } 150 | } 151 | return QSize(nMaxWidth+m_spaceWid, (nCount + 1) * m_lineHeight+2*m_lineHeight); 152 | } 153 | 154 | void QNChatMessage::paintEvent(QPaintEvent *event) 155 | { 156 | Q_UNUSED(event); 157 | 158 | QPainter painter(this); 159 | painter.setRenderHints(QPainter::Antialiasing | QPainter::SmoothPixmapTransform);//消锯齿 160 | painter.setPen(Qt::NoPen); 161 | painter.setBrush(QBrush(Qt::gray)); 162 | 163 | if(m_userType == User_Type::User_She) { // 用户 164 | //头像 165 | // painter.drawRoundedRect(m_iconLeftRect,m_iconLeftRect.width(),m_iconLeftRect.height()); 166 | painter.drawPixmap(m_iconLeftRect, m_leftPixmap); 167 | 168 | //框加边 169 | QColor col_KuangB(234, 234, 234); 170 | painter.setBrush(QBrush(col_KuangB)); 171 | painter.drawRoundedRect(m_kuangLeftRect.x()-1,m_kuangLeftRect.y()-1,m_kuangLeftRect.width()+2,m_kuangLeftRect.height()+2,4,4); 172 | //框 173 | QColor col_Kuang(255,255,255); 174 | painter.setBrush(QBrush(col_Kuang)); 175 | painter.drawRoundedRect(m_kuangLeftRect,4,4); 176 | 177 | //三角 178 | QPointF points[3] = { 179 | QPointF(m_sanjiaoLeftRect.x(), 30), 180 | QPointF(m_sanjiaoLeftRect.x()+m_sanjiaoLeftRect.width(), 25), 181 | QPointF(m_sanjiaoLeftRect.x()+m_sanjiaoLeftRect.width(), 35), 182 | }; 183 | QPen pen; 184 | pen.setColor(col_Kuang); 185 | painter.setPen(pen); 186 | painter.drawPolygon(points, 3); 187 | 188 | //三角加边 189 | QPen penSanJiaoBian; 190 | penSanJiaoBian.setColor(col_KuangB); 191 | painter.setPen(penSanJiaoBian); 192 | painter.drawLine(QPointF(m_sanjiaoLeftRect.x() - 1, 30), QPointF(m_sanjiaoLeftRect.x()+m_sanjiaoLeftRect.width(), 24)); 193 | painter.drawLine(QPointF(m_sanjiaoLeftRect.x() - 1, 30), QPointF(m_sanjiaoLeftRect.x()+m_sanjiaoLeftRect.width(), 36)); 194 | 195 | //内容 196 | QPen penText; 197 | penText.setColor(QColor(51,51,51)); 198 | painter.setPen(penText); 199 | QTextOption option(Qt::AlignLeft | Qt::AlignVCenter); 200 | option.setWrapMode(QTextOption::WrapAtWordBoundaryOrAnywhere); 201 | painter.setFont(this->font()); 202 | painter.drawText(m_textLeftRect, m_msg,option); 203 | } else if(m_userType == User_Type::User_Me) { // 自己 204 | //头像 205 | // painter.drawRoundedRect(m_iconRightRect,m_iconRightRect.width(),m_iconRightRect.height()); 206 | painter.drawPixmap(m_iconRightRect, m_rightPixmap); 207 | 208 | //框 209 | QColor col_Kuang(75,164,242); 210 | painter.setBrush(QBrush(col_Kuang)); 211 | painter.drawRoundedRect(m_kuangRightRect,4,4); 212 | 213 | //三角 214 | QPointF points[3] = { 215 | QPointF(m_sanjiaoRightRect.x()+m_sanjiaoRightRect.width(), 30), 216 | QPointF(m_sanjiaoRightRect.x(), 25), 217 | QPointF(m_sanjiaoRightRect.x(), 35), 218 | }; 219 | QPen pen; 220 | pen.setColor(col_Kuang); 221 | painter.setPen(pen); 222 | painter.drawPolygon(points, 3); 223 | 224 | //内容 225 | QPen penText; 226 | penText.setColor(Qt::white); 227 | painter.setPen(penText); 228 | QTextOption option(Qt::AlignLeft | Qt::AlignVCenter); 229 | option.setWrapMode(QTextOption::WrapAtWordBoundaryOrAnywhere); 230 | painter.setFont(this->font()); 231 | painter.drawText(m_textRightRect,m_msg,option); 232 | } else if(m_userType == User_Type::User_Time) { // 时间 233 | QPen penText; 234 | penText.setColor(QColor(153,153,153)); 235 | painter.setPen(penText); 236 | QTextOption option(Qt::AlignCenter); 237 | option.setWrapMode(QTextOption::WrapAtWordBoundaryOrAnywhere); 238 | QFont te_font = this->font(); 239 | te_font.setFamily("MicrosoftYaHei"); 240 | te_font.setPointSize(10); 241 | painter.setFont(te_font); 242 | painter.drawText(this->rect(),m_curTime,option); 243 | } 244 | } 245 | -------------------------------------------------------------------------------- /chatmessage/qnchatmessage.h: -------------------------------------------------------------------------------- 1 | /*------------------------------------------------- 2 | # 3 | # Project created by QtCreator 4 | # Author: 沙振宇 5 | # CreateTime: 2018-07-23 6 | # UpdateTime: 2019-12-27 7 | # Info: Qt5气泡式聊天框——QListWidget+QPainter实现 8 | # Url:https://shazhenyu.blog.csdn.net/article/details/81505832 9 | # Github:https://github.com/ShaShiDiZhuanLan/Demo_MessageChat_Qt 10 | # 11 | #-------------------------------------------------*/ 12 | #ifndef QNCHATMESSAGE_H 13 | #define QNCHATMESSAGE_H 14 | 15 | #include 16 | 17 | class QPaintEvent; 18 | class QPainter; 19 | class QLabel; 20 | class QMovie; 21 | 22 | class QNChatMessage : public QWidget 23 | { 24 | Q_OBJECT 25 | public: 26 | explicit QNChatMessage(QWidget *parent = nullptr); 27 | 28 | enum User_Type{ 29 | User_System,//系统 30 | User_Me, //自己 31 | User_She, //用户 32 | User_Time, //时间 33 | }; 34 | void setTextSuccess(); 35 | void setText(QString text, QString time, QSize allSize, User_Type userType); 36 | 37 | QSize getRealString(QString src); 38 | QSize fontRect(QString str); 39 | 40 | inline QString text() {return m_msg;} 41 | inline QString time() {return m_time;} 42 | inline User_Type userType() {return m_userType;} 43 | protected: 44 | void paintEvent(QPaintEvent *event); 45 | private: 46 | QString m_msg; 47 | QString m_time; 48 | QString m_curTime; 49 | 50 | QSize m_allSize; 51 | User_Type m_userType = User_System; 52 | 53 | int m_kuangWidth; 54 | int m_textWidth; 55 | int m_spaceWid; 56 | int m_lineHeight; 57 | 58 | QRect m_iconLeftRect; 59 | QRect m_iconRightRect; 60 | QRect m_sanjiaoLeftRect; 61 | QRect m_sanjiaoRightRect; 62 | QRect m_kuangLeftRect; 63 | QRect m_kuangRightRect; 64 | QRect m_textLeftRect; 65 | QRect m_textRightRect; 66 | QPixmap m_leftPixmap; 67 | QPixmap m_rightPixmap; 68 | QLabel* m_loading = Q_NULLPTR; 69 | QMovie* m_loadingMovie = Q_NULLPTR; 70 | bool m_isSending = false; 71 | }; 72 | 73 | #endif // QNCHATMESSAGE_H 74 | -------------------------------------------------------------------------------- /img.qrc: -------------------------------------------------------------------------------- 1 | 2 | 3 | img/Customer Copy.png 4 | img/CustomerService.png 5 | img/loading4.gif 6 | 7 | 8 | -------------------------------------------------------------------------------- /img/Customer Copy.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ShaShiDiZhuanLan/Demo_MessageChat_Qt/103428fb8080401e0bd6093aafc5e76b2b37effe/img/Customer Copy.png -------------------------------------------------------------------------------- /img/CustomerService.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ShaShiDiZhuanLan/Demo_MessageChat_Qt/103428fb8080401e0bd6093aafc5e76b2b37effe/img/CustomerService.png -------------------------------------------------------------------------------- /img/loading4.gif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ShaShiDiZhuanLan/Demo_MessageChat_Qt/103428fb8080401e0bd6093aafc5e76b2b37effe/img/loading4.gif -------------------------------------------------------------------------------- /main.cpp: -------------------------------------------------------------------------------- 1 | /*------------------------------------------------- 2 | # 3 | # Project created by QtCreator 4 | # Author: 沙振宇 5 | # CreateTime: 2018-07-23 6 | # UpdateTime: 2019-12-27 7 | # Info: Qt5气泡式聊天框——QListWidget+QPainter实现 8 | # Url:https://shazhenyu.blog.csdn.net/article/details/81505832 9 | # Github:https://github.com/ShaShiDiZhuanLan/Demo_MessageChat_Qt 10 | # 11 | #-------------------------------------------------*/ 12 | #include "mainwindow.h" 13 | #include 14 | 15 | int main(int argc, char *argv[]) 16 | { 17 | QApplication a(argc, argv); 18 | MainWindow w; 19 | w.show(); 20 | 21 | return a.exec(); 22 | } 23 | -------------------------------------------------------------------------------- /mainwindow.cpp: -------------------------------------------------------------------------------- 1 | /*------------------------------------------------- 2 | # 3 | # Project created by QtCreator 4 | # Author: 沙振宇 5 | # CreateTime: 2018-07-23 6 | # UpdateTime: 2019-12-27 7 | # Info: Qt5气泡式聊天框——QListWidget+QPainter实现 8 | # Url:https://shazhenyu.blog.csdn.net/article/details/81505832 9 | # Github:https://github.com/ShaShiDiZhuanLan/Demo_MessageChat_Qt 10 | # 11 | #-------------------------------------------------*/ 12 | #include "mainwindow.h" 13 | #include "ui_mainwindow.h" 14 | #include 15 | #include 16 | 17 | MainWindow::MainWindow(QWidget *parent) : 18 | QMainWindow(parent), 19 | ui(new Ui::MainWindow) 20 | { 21 | ui->setupUi(this); 22 | 23 | resize(600, 800); 24 | } 25 | 26 | MainWindow::~MainWindow() 27 | { 28 | delete ui; 29 | } 30 | 31 | void MainWindow::on_pushButton_clicked() 32 | { 33 | QString msg = ui->textEdit->toPlainText(); 34 | ui->textEdit->setText(""); 35 | QString time = QString::number(QDateTime::currentDateTime().toTime_t()); //时间戳 36 | 37 | bool isSending = true; // 发送中 38 | 39 | qDebug()<<"addMessage" << msg << time << ui->listWidget->count(); 40 | if(ui->listWidget->count()%2) { 41 | if(isSending) { 42 | dealMessageTime(time); 43 | 44 | QNChatMessage* messageW = new QNChatMessage(ui->listWidget->parentWidget()); 45 | QListWidgetItem* item = new QListWidgetItem(ui->listWidget); 46 | dealMessage(messageW, item, msg, time, QNChatMessage::User_Me); 47 | } else { 48 | bool isOver = true; 49 | for(int i = ui->listWidget->count() - 1; i > 0; i--) { 50 | QNChatMessage* messageW = (QNChatMessage*)ui->listWidget->itemWidget(ui->listWidget->item(i)); 51 | if(messageW->text() == msg) { 52 | isOver = false; 53 | messageW->setTextSuccess(); 54 | } 55 | } 56 | if(isOver) { 57 | dealMessageTime(time); 58 | 59 | QNChatMessage* messageW = new QNChatMessage(ui->listWidget->parentWidget()); 60 | QListWidgetItem* item = new QListWidgetItem(ui->listWidget); 61 | dealMessage(messageW, item, msg, time, QNChatMessage::User_Me); 62 | messageW->setTextSuccess(); 63 | } 64 | } 65 | } else { 66 | if(msg != "") { 67 | dealMessageTime(time); 68 | 69 | QNChatMessage* messageW = new QNChatMessage(ui->listWidget->parentWidget()); 70 | QListWidgetItem* item = new QListWidgetItem(ui->listWidget); 71 | dealMessage(messageW, item, msg, time, QNChatMessage::User_She); 72 | } 73 | } 74 | ui->listWidget->setCurrentRow(ui->listWidget->count()-1); 75 | } 76 | 77 | void MainWindow::dealMessage(QNChatMessage *messageW, QListWidgetItem *item, QString text, QString time, QNChatMessage::User_Type type) 78 | { 79 | messageW->setFixedWidth(this->width()); 80 | QSize size = messageW->fontRect(text); 81 | item->setSizeHint(size); 82 | messageW->setText(text, time, size, type); 83 | ui->listWidget->setItemWidget(item, messageW); 84 | } 85 | 86 | void MainWindow::dealMessageTime(QString curMsgTime) 87 | { 88 | bool isShowTime = false; 89 | if(ui->listWidget->count() > 0) { 90 | QListWidgetItem* lastItem = ui->listWidget->item(ui->listWidget->count() - 1); 91 | QNChatMessage* messageW = (QNChatMessage*)ui->listWidget->itemWidget(lastItem); 92 | int lastTime = messageW->time().toInt(); 93 | int curTime = curMsgTime.toInt(); 94 | qDebug() << "curTime lastTime:" << curTime - lastTime; 95 | isShowTime = ((curTime - lastTime) > 60); // 两个消息相差一分钟 96 | // isShowTime = true; 97 | } else { 98 | isShowTime = true; 99 | } 100 | if(isShowTime) { 101 | QNChatMessage* messageTime = new QNChatMessage(ui->listWidget->parentWidget()); 102 | QListWidgetItem* itemTime = new QListWidgetItem(ui->listWidget); 103 | 104 | QSize size = QSize(this->width(), 40); 105 | messageTime->resize(size); 106 | itemTime->setSizeHint(size); 107 | messageTime->setText(curMsgTime, curMsgTime, size, QNChatMessage::User_Time); 108 | ui->listWidget->setItemWidget(itemTime, messageTime); 109 | } 110 | } 111 | 112 | void MainWindow::resizeEvent(QResizeEvent *event) 113 | { 114 | Q_UNUSED(event); 115 | 116 | 117 | ui->textEdit->resize(this->width() - 20, ui->widget->height() - 20); 118 | ui->textEdit->move(10, 10); 119 | 120 | ui->pushButton->move(ui->textEdit->width()+ui->textEdit->x() - ui->pushButton->width() - 10, 121 | ui->textEdit->height()+ui->textEdit->y() - ui->pushButton->height() - 10); 122 | 123 | 124 | for(int i = 0; i < ui->listWidget->count(); i++) { 125 | QNChatMessage* messageW = (QNChatMessage*)ui->listWidget->itemWidget(ui->listWidget->item(i)); 126 | QListWidgetItem* item = ui->listWidget->item(i); 127 | 128 | dealMessage(messageW, item, messageW->text(), messageW->time(), messageW->userType()); 129 | } 130 | } 131 | -------------------------------------------------------------------------------- /mainwindow.h: -------------------------------------------------------------------------------- 1 | /*------------------------------------------------- 2 | # 3 | # Project created by QtCreator 4 | # Author: 沙振宇 5 | # CreateTime: 2018-07-23 6 | # UpdateTime: 2019-12-27 7 | # Info: Qt5气泡式聊天框——QListWidget+QPainter实现 8 | # Url:https://shazhenyu.blog.csdn.net/article/details/81505832 9 | # Github:https://github.com/ShaShiDiZhuanLan/Demo_MessageChat_Qt 10 | # 11 | #-------------------------------------------------*/ 12 | #ifndef MAINWINDOW_H 13 | #define MAINWINDOW_H 14 | 15 | #include 16 | #include 17 | #include "chatmessage/qnchatmessage.h" 18 | 19 | namespace Ui { 20 | class MainWindow; 21 | } 22 | 23 | class MainWindow : public QMainWindow 24 | { 25 | Q_OBJECT 26 | 27 | public: 28 | explicit MainWindow(QWidget *parent = 0); 29 | ~MainWindow(); 30 | 31 | void dealMessage(QNChatMessage *messageW, QListWidgetItem *item, QString text, QString time, QNChatMessage::User_Type type); 32 | void dealMessageTime(QString curMsgTime); 33 | protected: 34 | void resizeEvent(QResizeEvent *event); 35 | private slots: 36 | void on_pushButton_clicked(); 37 | 38 | private: 39 | Ui::MainWindow *ui; 40 | }; 41 | 42 | #endif // MAINWINDOW_H 43 | -------------------------------------------------------------------------------- /mainwindow.ui: -------------------------------------------------------------------------------- 1 | 2 | 3 | MainWindow 4 | 5 | 6 | 7 | 0 8 | 0 9 | 693 10 | 626 11 | 12 | 13 | 14 | MainWindow 15 | 16 | 17 | 18 | 19 | 0 20 | 21 | 22 | 0 23 | 24 | 25 | 0 26 | 27 | 28 | 0 29 | 30 | 31 | 0 32 | 33 | 34 | 35 | 36 | 37 | 0 38 | 0 39 | 40 | 41 | 42 | QListWidget{background-color: rgb(247, 247, 247); color:rgb(51,51,51); border: 1px solid rgb(247, 247, 247);outline:0px;} 43 | QListWidget::Item{background-color: rgb(247, 247, 247);} 44 | QListWidget::Item:hover{background-color: rgb(247, 247, 247); } 45 | QListWidget::item:selected{ 46 | background-color: rgb(247, 247, 247); 47 | color:black; 48 | border: 1px solid rgb(247, 247, 247); 49 | } 50 | QListWidget::item:selected:!active{border: 1px solid rgb(247, 247, 247); background-color: rgb(247, 247, 247); color:rgb(51,51,51); } 51 | 52 | 53 | Qt::ScrollBarAsNeeded 54 | 55 | 56 | Qt::ScrollBarAlwaysOff 57 | 58 | 59 | 60 | 61 | 62 | 63 | 64 | 0 65 | 200 66 | 67 | 68 | 69 | 70 | 16777215 71 | 200 72 | 73 | 74 | 75 | 76 | 77 | 10 78 | 10 79 | 561 80 | 151 81 | 82 | 83 | 84 | 85 | 0 86 | 0 87 | 88 | 89 | 90 | 91 | 92 | 93 | 440 94 | 120 95 | 81 96 | 23 97 | 98 | 99 | 100 | 发送 101 | 102 | 103 | 104 | 105 | 106 | 107 | 108 | 109 | 110 | 111 | 112 | -------------------------------------------------------------------------------- /效果1.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ShaShiDiZhuanLan/Demo_MessageChat_Qt/103428fb8080401e0bd6093aafc5e76b2b37effe/效果1.png -------------------------------------------------------------------------------- /效果2.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ShaShiDiZhuanLan/Demo_MessageChat_Qt/103428fb8080401e0bd6093aafc5e76b2b37effe/效果2.png --------------------------------------------------------------------------------