40.qt quick- 高仿微信实现局域网聊天V4版本(支持gif动图表情包、消息聊天、拖动缩放窗口)
在上章37.qt quick- 高仿微信实现局域网聊天V3版本(添加登录界面、UDP校验登录、皮肤更换、3D旋转),我们已经实现了:
- 添加登录界面、
- UDP校验登录、
- 皮肤更换、
- 3D旋转(主界面和登录界面之间切换) 、
所以本章实现:
- 2、支持拖动和更改窗口大小、
- 3、可以单独聊天、也可以在聊天室所有人聊天、
- 4、支持收发gif表情包(支持粘贴复制)、
- 5、自动刷新当前好友在线人数等、
1.界面展示
界面布局如下所示:

界面截图如下所示:

效果图如下所示:

有点大,可能加载不了,不过已经上传到bilibili了https://www.bilibili.com/video/BV1Ao4y1S7zX
由于代码量有点多,所以讲解重点的部分
2.Text中的gif管理
2.1 在Text中加入一个gif
// 添加一个gif
void GifTextHandler::inset(QString fileName)
{
if (!m_documnt)
return; QTextCursor cursor = QTextCursor(m_documnt->textDocument());
cursor.setPosition(m_cursorStart);
if (m_cursorStart > m_cursorEnd) {
cursor.setPosition(0);
} else if (m_cursorStart != m_cursorEnd)
cursor.setPosition(m_cursorEnd, QTextCursor::KeepAnchor); addMovie(fileName); // 通过QMovie加载一个gif
QTextImageFormat imageFormat;
imageFormat.setName(fileName);
imageFormat.setWidth(m_width);
imageFormat.setHeight(m_height);
cursor.insertImage(imageFormat, QTextFrameFormat::InFlow); }
添加一个gif后,又如何去管理这个gif,假如我当前文本把这个gif删除了,那么这个QMovie也应该释放才行,否则就内存溢出啦.
2.2 gif动态释放与管理
所以每隔1秒就去读取下文本,gif是否还存在,如果不存在则释放QMovie:
QList<QString> list = m_movies.keys();
for(int i=0;i<list.length();i++) {
m_movies[list[i]]->setProperty("status",false);
} QTextBlock block = m_documnt->textDocument()->firstBlock();
QVector<QTextFormat> allFormats = m_documnt->textDocument()->allFormats();
while(block.isValid()) {
QTextBlockFormat blockFmt = block.blockFormat();
for(QTextBlock::iterator it = block.begin(); !it.atEnd(); it++) {
QTextCharFormat charFmt = it.fragment().charFormat(); // fragment是存放文字,位置的类
if (charFmt.objectType() == QTextFormat::ImageObject) {
QString file = charFmt.property(QTextFormat::ImageName).toString();
if (!m_movies.contains(file)) {
addMovie(file);
}
m_movies[file]->setProperty("status",true);
// qDebug()<<"status: "<<file;
}
}
block = block.next();
} for(int i=0;i<list.length();i++) {
if (m_movies[list[i]]->property("status") == false) {
qDebug()<<"释放gif:"<<i<<list[i];
delete m_movies[list[i]];
m_movies.remove(list[i]);
continue;
}
}
如果文本释放了,那么就遍历整个gif表,释放所有:
GifTextHandler::~GifTextHandler() // 释放资源
{
QList<QString> list = m_movies.keys();
for(int i=0;i<list.length();i++) { // 释放所有GIF
delete m_movies[list[i]];
}
m_movies.clear();
}
2.3 文本发送
由于文本中包含了gif图,所以发送的时候,我们需要将文本内容(包含gif)进行编码,转换成字符串,代码如下所示:
QString GifTextHandler::coding()
{
QString ret("");
QTextBlock block = m_documnt->textDocument()->firstBlock();
QVector<QTextFormat> allFormats = m_documnt->textDocument()->allFormats(); while(block.isValid()) {
for(QTextBlock::iterator it = block.begin(); !it.atEnd(); it++) {
QTextCharFormat charFmt = it.fragment().charFormat(); // fragment是存放文字,位置的类
if (charFmt.objectType() == QTextFormat::ImageObject) {
ret.append("["+charFmt.property(QTextFormat::ImageName).toString().remove(GIF_PREFIXDIR)+"]");
} else {
ret.append(it.fragment().text());
}
}
if (block.next().isValid()) // QTextBlock以换行为分割成每个块,所以每次块遍历完后需要加换行符
ret.append("\r\n"); block = block.next();
}
return ret;
}
2.4 文本接收
由于接收到的数据是一段字符串,所以我们需要解码,将gif标志显示成一个gif动图,代码如下所示:
void GifTextHandler::encoding(QString text)
{
if (!m_documnt) {
qDebug()<<"encoding: m_documnt == nullptr";
return;
}
QTextCursor cursor = QTextCursor(m_documnt->textDocument());
cursor.setPosition(0);
QRegularExpression re("\\[\\d+\\.gif\\]");
QRegularExpressionMatch match;
int offset = 0;
int fileNum;
QString file;
QString ret;
while (offset < text.length()) {
match = re.match(text,offset);
if (match.hasMatch()) {
sscanf(match.captured(0).toLocal8Bit(),"[%d.gif]", &fileNum);
file = QString(GIF_PREFIXDIR+"%1.gif").arg(fileNum);
cursor.insertText(text.mid(offset, match.capturedStart(0) - offset)); // 添加前面的
ret.append(text.mid(offset, match.capturedStart(0) - offset));
addMovie(file);
QTextImageFormat imageFormat;
imageFormat.setName(file);
imageFormat.setWidth(m_width);
imageFormat.setHeight(m_height);
cursor.insertImage(imageFormat, QTextFrameFormat::InFlow);
offset = match.capturedEnd(0);
} else {
ret.append(text.mid(offset, text.length() - offset));
cursor.insertText(text.mid(offset, text.length() - offset)); // 添加前面的
break;
}
}
}
3.C++ QAbstractListModel类
qml中使用的是ListView,而model数据是使用C++ model(继承于QAbstractListModel).
所以当有好友上线时,我们需要往model中添加一行好友数据,并通知ListView刷新局部数据,代码如下所示:
void FriendModel::addFriend(MessageDesc* msg)
{
for (int i=1; i < m_data.count(); i++) {
if (m_data[i]->title() == msg->srcUser) {
return;
}
}
// 将头像 Arr数据转换成jpg文件
QPixmap pixmap;
QString str = AppCfg::getInstance()->read(AppCfg::FriendHeadDir).remove("file:///")+msg->srcUser+".jpg";
qDebug()<<msg->headArr.length()<<pixmap.loadFromData((uchar *)msg->headArr.data(), msg->headArr.length());
bool ret = pixmap.save(str);
AppCfg::getInstance()->fileLogWrite(QString("addFriend str%1 str%2 ret%3").arg(str).arg(QUrl::fromLocalFile(str).toString()).
arg(ret)); beginInsertRows(QModelIndex(), 1, 1); // 由于聊天室始终顶置,所以只能插入到第2行
m_data.insert(1, new FriendChatData(msg->srcUser, QUrl::fromLocalFile(str).toString()));
endInsertRows();
hintInsetChatRoom("\""+msg->srcUser+"\" 于"+QDateTime::currentDateTime().toString(" hh:mm ")+"上线!");
}
当好友下线时,则需要删除好友数据,代码如下所示:
void FriendModel::removeFriend(MessageDesc* msg)
{
for (int i=1; i < m_data.count(); i++) {
if (m_data[i]->title() == msg->srcUser) {
beginRemoveRows(QModelIndex(), i, i); // 假如聊天界面的model等于当前下线的好友,那么需要释放聊天model数据,并刷新视图
if (m_chat != NULL && m_chat->data() == m_data[i]) {
delete m_chat;
m_chat = NULL;
emit removeFriendcloseChat();
}
delete m_data[i];
m_data.removeAt(i);
endRemoveRows();
hintInsetChatRoom("\""+msg->srcUser+"\" 于"+QDateTime::currentDateTime().toString(" hh:mm ")+"下线!");
break;
}
}
}
其它的好友收发消息,则依葫芦画瓢,通通实现一遍即可.
40.qt quick- 高仿微信实现局域网聊天V4版本(支持gif动图表情包、消息聊天、拖动缩放窗口)的更多相关文章
- 28.qt quick-ListView高仿微信好友列表和聊天列表
1.视图模型介绍 在Qml中.常见的View视图有: ListView: 列表视图,视图中数据来自ListModel.XmlListModel或c++中继承自QAbstractItemModel或Q ...
- 31.qt quick-使用SwipeView添加滑动视图-高仿微信V2版本
在上章我们学习了ListView,然后实现了: 28.qt quick-ListView高仿微信好友列表和聊天列表,本章我们来学习SwipeView滑动视图,并出高仿微信V2版本: 1.Contain ...
- iOS天气动画、高仿QQ菜单、放京东APP、高仿微信、推送消息等源码
iOS精选源码 TYCyclePagerView iOS上的一个无限循环轮播图组件 iOS高仿微信完整项目源码 想要更简单的推送消息,看本文就对了 ScrollView嵌套ScrolloView解决方 ...
- 转-Fragment+ViewPager组件(高仿微信界面)
http://www.cnblogs.com/lichenwei/p/3982302.html 什么是ViewPager? 关于ViewPager的介绍和使用,在之前我写过一篇相关的文章<安卓开 ...
- GSD_WeiXin(高仿微信)应用源码
高仿微信计划:已经实现功能 1.微信首页(cell侧滑编辑.下拉眼睛动画.下拉拍短视频.点击进入聊天详情界面) 2.通讯录(联系人字母排序.搜索界面) 3.发现(朋友圈) 4.我(界面) 待实现功能( ...
- Android高仿微信(一)——如何消除启动时的白屏
默认情况下,APP启动时会先把屏幕刷成白色,然后才绘制第一个Activity中的View,这两个步骤之间的延迟会造成启动后先看到白屏(时间大概为1秒左右).时间不长,但是我们也看到,一般的APP时不存 ...
- 安卓开发笔记——Fragment+ViewPager组件(高仿微信界面)
什么是ViewPager? 关于ViewPager的介绍和使用,在之前我写过一篇相关的文章<安卓开发复习笔记——ViewPager组件(仿微信引导界面)>,不清楚的朋友可以看看,这里就不再 ...
- 实例源码--IOS高仿微信打飞机游戏(完整功能)
下载源码 技术要点: 1. IOS游戏开发基础框架 2. 高仿打飞机游戏 3. 游戏背景音频技术 4.源码详细的中文注释 ……. 详细介绍: 1. IOS游戏开发基础框架 此套源码为涉及IOS游戏开发 ...
- Android 高仿微信实时聊天 基于百度云推送
转载请标明出处:http://blog.csdn.net/lmj623565791/article/details/38799363 ,本文出自:[张鸿洋的博客] 一直在仿微信界面,今天终于有幸利用百 ...
随机推荐
- JAVA并发(6)-并发队列ArrayBlockingQueue
本文讲ArrayBlockingQueue 1. 介绍 一个基于数组的有界阻塞队列,FIFO顺序.支持等待消费者和生产者线程的可选公平策略(默认是非公平的).公平的话通常会降低吞吐量,但是可以减少可变 ...
- 使用adb命令查看APP包名 和 包入口方法
方法一: 1.查看 前台应用 包名,使用此命令注意:手机只启动要查看包名的app adb shell dumpsys activity | find "mFocusedActivity&qu ...
- 用Java如何设计一个阻塞队列,然后说说ArrayBlockingQueue和LinkedBlockingQueue
前言 用Java如何设计一个阻塞队列,这个问题是在面滴滴的时候被问到的.当时确实没回答好,只是说了用个List,然后消费者再用个死循环一直去监控list的是否有值,有值的话就处理List里面的内容.回 ...
- 十一、设置Nginx开机自启动
一.创建nginx.service文件 [root@svr7 ~]# vim /lib/systemd/system/nginx.service [Unit]Description=nginx ser ...
- 带你了解Java的序列化与反序列化
什么是序列化 序列化:将 Java 对象转换成字节流的过程. 什么是反序列化 反序列化:将字节流转换成 Java 对象的过程. 序列化的实现 当 Java 对象需要在网络上传输 或者 持久化存储到文件 ...
- 轻松吃透实时时钟芯片DS1302软硬件设计,看完秒懂
今天我们来讨论一款老掉牙的实时时钟芯片DS1302.什么是实时时钟(RealTime Clock, RTC)呢?为什么我们需要它呢?假设你使用单片机实现万年历应用,一般的做法是这样的:设置中断后判断1 ...
- 为什么PMOS比NMOS的沟道导通电阻大,速度慢,价格高-透彻详解
原文地址点击这里: 在前一节,我们对PMOS与NMOS两种增强型场效应管的开关电路作了详细的介绍, 并且还提到过一种广为流传的说法:相对于NMOS管,PMOS管的沟道导通电阻更大.速度更慢.成本更高等 ...
- 入门Kubernetes - YAML文件
前言 前篇文章中简单了解到如何把.Net Core 程序部署到k8s中,过程中使用了多个*.yaml文件,那么这些文件的格式及含义.语法是如何的呢? 接下来,进一步了解学习 一.YAML介绍: 1.简 ...
- 03 Git 以及 其 GUI TortoiseGit 的下载与安装
前面也说过嘛,要紧跟大佬们的步伐--选择最受欢迎的版本控制系统. 而根据 [JetBrains](JetBrains: Essential tools for software developers ...
- 33、awk命令详解
33.1.命令介绍: awk不仅仅是linux系统中的一个命令,而且是一种编程语言,可以用来处理数据和生成报告. awk的数据可以是一个或多个文件,可以是来自标准输入,也可以通过管道获取标准输入,aw ...