在上章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动图表情包、消息聊天、拖动缩放窗口)的更多相关文章

  1. 28.qt quick-ListView高仿微信好友列表和聊天列表

    1.视图模型介绍  在Qml中.常见的View视图有: ListView: 列表视图,视图中数据来自ListModel.XmlListModel或c++中继承自QAbstractItemModel或Q ...

  2. 31.qt quick-使用SwipeView添加滑动视图-高仿微信V2版本

    在上章我们学习了ListView,然后实现了: 28.qt quick-ListView高仿微信好友列表和聊天列表,本章我们来学习SwipeView滑动视图,并出高仿微信V2版本: 1.Contain ...

  3. iOS天气动画、高仿QQ菜单、放京东APP、高仿微信、推送消息等源码

    iOS精选源码 TYCyclePagerView iOS上的一个无限循环轮播图组件 iOS高仿微信完整项目源码 想要更简单的推送消息,看本文就对了 ScrollView嵌套ScrolloView解决方 ...

  4. 转-Fragment+ViewPager组件(高仿微信界面)

    http://www.cnblogs.com/lichenwei/p/3982302.html 什么是ViewPager? 关于ViewPager的介绍和使用,在之前我写过一篇相关的文章<安卓开 ...

  5. GSD_WeiXin(高仿微信)应用源码

    高仿微信计划:已经实现功能 1.微信首页(cell侧滑编辑.下拉眼睛动画.下拉拍短视频.点击进入聊天详情界面) 2.通讯录(联系人字母排序.搜索界面) 3.发现(朋友圈) 4.我(界面) 待实现功能( ...

  6. Android高仿微信(一)——如何消除启动时的白屏

    默认情况下,APP启动时会先把屏幕刷成白色,然后才绘制第一个Activity中的View,这两个步骤之间的延迟会造成启动后先看到白屏(时间大概为1秒左右).时间不长,但是我们也看到,一般的APP时不存 ...

  7. 安卓开发笔记——Fragment+ViewPager组件(高仿微信界面)

    什么是ViewPager? 关于ViewPager的介绍和使用,在之前我写过一篇相关的文章<安卓开发复习笔记——ViewPager组件(仿微信引导界面)>,不清楚的朋友可以看看,这里就不再 ...

  8. 实例源码--IOS高仿微信打飞机游戏(完整功能)

    下载源码 技术要点: 1. IOS游戏开发基础框架 2. 高仿打飞机游戏 3. 游戏背景音频技术 4.源码详细的中文注释 ……. 详细介绍: 1. IOS游戏开发基础框架 此套源码为涉及IOS游戏开发 ...

  9. Android 高仿微信实时聊天 基于百度云推送

    转载请标明出处:http://blog.csdn.net/lmj623565791/article/details/38799363 ,本文出自:[张鸿洋的博客] 一直在仿微信界面,今天终于有幸利用百 ...

随机推荐

  1. postman实现参数化执行及断言处理

    一.假设需要做的测试的参数如下: 注意保存为.csv文件时一定要选择格式为UTF-8 ,避免乱码. 二.输入参数和期望结果在postman中的用法: 注意一定要通过runner的方式进行运行,选择对应 ...

  2. MapReduce —— MapTask阶段源码分析(Input环节)

    不得不说阅读源码的过程,极其痛苦 .Dream Car 镇楼 ~ ! 虽说整个MapReduce过程也就只有Map阶段和Reduce阶段,但是仔细想想,在Map阶段要做哪些事情?这一阶段具体应该包含数 ...

  3. 尚硅谷Java——宋红康笔记【day1-day5】

    day1 注释 1.java规范的三种注释方式: 单行注释 多行注释 文档注释(java特有) 2. 单行注释和多行注释的作用: ① 对所写的程序进行解释说明,增强可读性.方便自己,方便别人 ② 调试 ...

  4. NX二次开发-获取WCS标识

    函数:UF_CSYS_ask_wcs() 函数说明:获取工作坐标系对象的标识. 用法: 1 #include <uf.h> 2 #include <uf_csys.h> 3 e ...

  5. csp-s模拟测试59(10.4)「Reverse」(set)·「Silhouette」(容斥)

    A. Reverse 菜鸡wwb又不会了..... 可以线段树优化建边,然而不会所以只能set水了 发现对于k和当前反转点固定的节点x确定奇偶性所到达的节点奇偶性是一定的 那么set维护奇偶点,然后每 ...

  6. redis学习第二天

    Redis 在 2.8.9 版本之后添加了 HyperLogLog 结构 Redis HyperLogLog 是用来做基数统计的算法,HyperLogLog 的优点是,在输入元素的数量或者体积非常非常 ...

  7. 安卓控件RecycleView的简单使用

    RecycleView的使用 目录 RecycleView的使用 技术概述 技术详述 遇到问题和解决 总结 参考文献 技术概述 RecycleView是谷歌官方对ListView的改进(并不是替代), ...

  8. Java行为参数化的演进

    首先感谢<java8实战>一书作者某某某. 需求场景: 为一位果农设计一款软件,可以根据果农的需求筛选出相应的水果. 例如: 根据颜色筛选 根据重量筛选 根据颜色和重量筛选 准备工作 定义 ...

  9. QT 之 ODBC连接人大金仓数据库

    QT 之 使用 ODBC 驱动连接人大金仓数据库 获取数据库驱动和依赖动态库 此操作可在人大金仓官网下载与系统匹配的接口动态库,或者从架构数据库的源码中获取驱动和依赖动态库 分别为: 驱动动态库:kd ...

  10. Jquery手机点击其他地方隐藏控件问题

    因为不太懂mui的底部导航栏的操作,所以自己写了用很普通的方法实现手机底部导航栏,遇到了很多问题.比如:要实现点击底部菜单栏上某一个菜单,显示子菜单,然后点击手机空白处,隐藏菜单. 实现方法是: // ...