个人信息窗口
基本架构

基础信息显示设计



信号槽连接:对于个人信息窗口上的按钮功能,通过信号槽进行连接。目标是实现可修改信息


用户信息窗口
窗口架构
- 申请好友:如果不是好友才可以使用
- 下面三个按钮的逻辑具体实现在服务端实现后再进行具体实现

点击好友头像后,出现资料卡逻辑设置,借助槽函数实现


界面布局实现


会话详情窗口
窗口设计
- 具体实现,分别以单聊和群聊两种的设计

界面布局实现

细节问题:名字截断逻辑实现
- 设置可以放置名字的最大宽度
- 计算文本的长度是多少

按钮和信号槽连接


选择好友窗口
架构设计,基于会话详情窗口的点击按钮,然后弹出一个新的会话窗

界面布局实现


添加群聊信号槽的实现


好友窗口设计,先实现一个好友信息,然后再实现排列的所有信息

细节实现:实现鼠标悬停状态下,用户信息颜色变深



细节分析:选择好友后,右侧窗口显示逻辑实现


左右侧好友增删操作的实现


程序崩溃退出:实现电点击左侧按钮,可以成功加入其到右侧,但是当点击右侧按钮的时候,程序异常退出

- 经调试,判断异常位置

- 根据上述代码,此处如果直接删除chooseFriendItem,一定会导致checkBox崩溃

- 解决:信号槽在执行完槽函数之后,内部还会做一些其他工作,需要首先释放对象,然后再delete,不可以直接delete

群聊会话详情窗口
窗口架构设计:仍在会话详情页面中进行显示

修改实现逻辑,弹出窗口的时候,需要分辨其是单聊还是群聊会话

群聊添加成员操作:获取成员信息,并按照4*4进行排列
- 记录当前添加的行数与列数
- 然后借助循环逻辑,实现4*4网格布局



实现代码

添加好友窗口
窗口布局设计

连接中间布局的添加按钮,通过信号槽,实现点击添加就可以弹出该窗口

单个好友搜索结果的实现

cpp
///一个好友搜索的结果
FriendResultItem::FriendResultItem(const UserInfo &userInfo) :userInfo(userInfo)
{
//1. 设置基本属性
this->setFixedHeight(70);
this->setSizePolicy(QSizePolicy::Expanding , QSizePolicy::Fixed);
//2. 布局管理器
QGridLayout*layout = new QGridLayout();
layout->setVerticalSpacing(0);
layout->setHorizontalSpacing(10);
layout->setContentsMargins(0,0,20,0);
this->setLayout(layout);
//3.头像
QPushButton*avatarBtn = new QPushButton();
avatarBtn->setFixedSize(50,50);
avatarBtn->setIconSize(QSize(50,50));
avatarBtn->setIcon(userInfo.avatar);
//4. 昵称
QLabel* nameLabel = new QLabel();
nameLabel->setFixedHeight(35);
nameLabel->setSizePolicy(QSizePolicy::Expanding, QSizePolicy::Fixed);
nameLabel->setAlignment(Qt::AlignLeft | Qt::AlignVCenter);
nameLabel->setStyleSheet("QLabel { font-size: 16px; font-weight: 700;}");
nameLabel->setText(userInfo.nickname);
// 5. 个性签名
QLabel* descLabel = new QLabel();
descLabel->setFixedHeight(35);
descLabel->setSizePolicy(QSizePolicy::Expanding, QSizePolicy::Fixed);
descLabel->setAlignment(Qt::AlignLeft | Qt::AlignVCenter);
descLabel->setStyleSheet("QLabel { font-size: 14px; }");
descLabel->setText(userInfo.description);
//6 添加好友按钮
addBtn = new QPushButton();
addBtn->setFixedSize(100,40);
addBtn->setText("添加好友");
QString btnStyle = "QPushButton { border: none; background-color: rgb(137, 217, 97); color: rgb(255, 255, 255); border-radius: 10px;} ";
btnStyle += "QPushButton:pressed { background-color: rgb(200, 200, 200); }";
addBtn->setStyleSheet(btnStyle);
//添加到布局管理器中
layout->addWidget(avatarBtn,0,0,2,1);
layout->addWidget(nameLabel,0,1);
layout->addWidget(descLabel,1,1);
layout->addWidget(addBtn,0,2,2,1);
}
新增和清除好友逻辑
cpp
///窗口中新增好友搜索结果
void AddFriendDialog::addResult(const UserInfo &userInfo)
{
FriendResultItem*item = new FriendResultItem(userInfo);
resultContainer->layout()->addWidget(item);
}
///清空界面所有结果
void AddFriendDialog::clear()
{
//逻辑:从后往前遍历窗口元素,然后进行删除
QVBoxLayout*layout = dynamic_cast<QVBoxLayout*>(resultContainer->layout());
for(int i =layout->count()-1;i>=0;--i)
{
QLayoutItem*layoutItem = layout->takeAt(i);
if(layoutItem==nullptr || layoutItem->widget()==nullptr)
{
continue;
}
delete layoutItem->widget();
}
}
主页面搜索框中输入数据,同步显示到弹出窗口的搜索框中



搜索好友窗口


历史消息窗口
窗口设计

窗口弹出逻辑

历史消息切换逻辑实现:点击关键词和点击时间查询不同的结果



滚动区域设计实现

单条消息的设计实现 ,主要逻辑是利用工厂模式,构建不同的消息类型

窗口中添加和清除历史消息逻辑实现

最终效果实现

cpp
///历史消息总窗口
HistoryMessageWidget::HistoryMessageWidget(QWidget *parent) :QDialog(parent)
{
//1.窗口基本属性
this->setFixedSize(600,600);
this->setWindowTitle("历史消息");
this->setWindowIcon(QIcon(":/resource/image/logo.png"));
this->setStyleSheet("QWidget { background-color: rgb(255, 255, 255); }");
this->setAttribute(Qt::WA_DeleteOnClose);
//2.创建布局管理器
QGridLayout*layout = new QGridLayout();
layout->setSpacing(10);
layout->setContentsMargins(30,30,30,0);
this->setLayout(layout);
//3.单选按钮
keyRadioBtn = new QRadioButton();
timeRadioBtn = new QRadioButton();
keyRadioBtn->setText("按关键字查询");
timeRadioBtn->setText("按时间查询");
keyRadioBtn->setChecked(true); //默认按照关键字查询
layout->addWidget(keyRadioBtn,0,0,1,2);
layout->addWidget(timeRadioBtn,0,2,1,2);
//4.搜索框
searchEdit = new QLineEdit();
searchEdit->setFixedHeight(50);
searchEdit->setSizePolicy(QSizePolicy::Expanding,QSizePolicy::Fixed);
searchEdit->setPlaceholderText("要搜索的关键词");
searchEdit->setStyleSheet("QLineEdit { border: none; border-radius: 10px; background-color: rgb(240, 240, 240); font-size: 16px; padding-left: 10px; }");
layout->addWidget(searchEdit,1,0,1,8);
//5.搜索按钮
QPushButton*searchBtn = new QPushButton();
searchBtn->setFixedSize(50,50);
searchBtn->setSizePolicy(QSizePolicy::Fixed,QSizePolicy::Fixed);
searchBtn->setIconSize(QSize(30, 30));
searchBtn->setIcon(QIcon(":/resource/image/search.png"));
QString btnStyle = "QPushButton { border: none; background-color: rgb(240, 240, 240); border-radius: 10px; }";
btnStyle += "QPushButton:pressed { background-color: rgb(220, 220, 220); }";
searchBtn->setStyleSheet(btnStyle);
layout->addWidget(searchBtn, 1, 8, 1, 1);
//6. 时间控件
QLabel*begTag = new QLabel();
begTag->setText("开始时间");
QLabel*endTag = new QLabel();
endTag->setText("结束时间");
begTimeEdit = new QDateTimeEdit();
endTimeEdit = new QDateTimeEdit();
begTimeEdit->setDisplayFormat("yyyy-MM-dd hh:mm");
endTimeEdit->setDisplayFormat("yyyy-MM-dd hh:mm");
begTimeEdit->setFixedHeight(40);
endTimeEdit->setFixedHeight(40);
begTag->hide();
endTag->hide();
begTimeEdit->hide();
endTimeEdit->hide();
//7. 滚动区域
initScrollArea(layout);
//8. 槽函数
connect(keyRadioBtn,&QRadioButton::clicked,this,[=](){
//时间控件隐藏
layout->removeWidget(begTag);
layout->removeWidget(begTimeEdit);
layout->removeWidget(endTag);
layout->removeWidget(endTimeEdit);
begTag->hide();
begTimeEdit->hide();
endTag->hide();
endTimeEdit->hide();
//关键词搜索框加入布局
layout->addWidget(searchEdit,1,0,1,8);
searchEdit->show();
});
connect(timeRadioBtn,&QRadioButton::clicked,this,[=](){
//关键字搜索框隐藏
layout->removeWidget(searchEdit);
searchEdit->hide();
//时间控件添加到布局管理器中
layout->addWidget(begTag,1,0,1,1);
layout->addWidget(begTimeEdit,1,1,1,3);
layout->addWidget(endTag,1,4,1,1);
layout->addWidget(endTimeEdit,1,5,1,3);
begTag->show();
begTimeEdit->show();
endTag->show();
endTimeEdit->show();
});
//测试
#if TEST_UI
for(int i =0;i<30;++i)
{
UserInfo sender;
sender.userId = "";
sender.nickname = "张三"+QString::number(i);
sender.avatar = QIcon(":/resource/image/defaultAvatar.png");
sender.description ="";
// Message message = Message::makeMessage(TEXT_TYPE,"",sender,QString("测试消息"+QString::number(i).toUtf8(),"");
Message message = Message::makeMessage(TEXT_TYPE, "", sender, QString("消息内容" + QString::number(i)).toUtf8(), "");
this->addHistoryMessage(message);
}
#endif
}
登陆窗口
窗口总体布局

登录窗口弹出逻辑分析
- 修改主函数逻辑,按照规则弹出登录窗口

注册与登录模式的切换,借助槽函数实现


账号登录与手机号登录切换逻辑实现

登录和注册窗口切换逻辑实现



用户名和手机号登录窗口切换逻辑分析



全局通知窗口
页面布局逻辑

全局通知窗口实现

cpp
Toast::Toast(const QString &text)
{
//1.窗口基本属性
this->setFixedSize(500,100);
this->setWindowTitle("消息通知");
this->setWindowIcon(QIcon(":/resource/image/logo.png"));
this->setAttribute(Qt::WA_DeleteOnClose);
this->setStyleSheet("QDialog {background-color:rgb(255,255,255);}");
this->setWindowFlags(Qt::FramelessWindowHint);
//2. 窗口尺寸
QScreen*screen = QApplication::primaryScreen();
int width = screen->size().width();
int height = screen->size().height();
int x = (width - this->width())/2;
int y = height - this->height()-100;
this->move(x,y);
//3. 布局管理器
QVBoxLayout*layout = new QVBoxLayout();
layout->setSpacing(0);
layout->setContentsMargins(0,0,0,0);
this->setLayout(layout);
//4. 显示文本
QLabel*label = new QLabel();
label->setSizePolicy(QSizePolicy::Expanding, QSizePolicy::Expanding);
label->setAlignment(Qt::AlignCenter);
label->setStyleSheet("QLabel { font-size: 32px; }");
label->setText(text);
layout->addWidget(label);
//5. 定时关闭
QTimer*timer = new QTimer(this);
connect(timer,&QTimer::timeout,this,[=](){
timer->stop();
this->close();
});
timer->start(2000);
}
void Toast::showMessage(const QString &text)
{
Toast*toast = new Toast(text);
toast->show();
}