本文未经授权,禁止转载
Day3回顾-登录注册功能
在Day3中,我们完成了以下功能
1、为了实现登陆注册功能,在数据库中添加了用户信息表等,并在QT中连接了数据库
2、完成了登录与注册的功能
3、完成了客户端下线的处理
Day4要完成的内容
1、完成好友界面和文件界面的设计
2、完成查找用户的功能
3、完成显示在线用户的功能
一、UI界面设计
在用户完成登陆后,就应该跳转到首页进行后续操作,因此我们需要完成首页以及其他界面的UI界面设计。
在客户端项目中创建 Qt 设计师界面类Index、 Friend、File,分别对应首页、好友界面、文件界面
1、选择项目文件 --- > 右键 ----> add New
2、选择Qt ---》 Qt 设计师界面类
3、选择 Widget 类
4、 输入类名,注意路径
5、注意项目路径,别添加到其他项目中
Friend和File的添加同Index一致
1.1 好友界面设计
添加完成之后,就会发现,项目中多了这几个文件。
1、 点击friend.ui 进入 好友界面
(1)添加5个按钮(QPushButton),并按照不同的按钮功能进行命名
(2)添加一个列表框(QListWidget),命名为 friend_LW
2、进行排版
其中、列表框(QListWidget)的QFrame中的frameShape属性设置为NoFrame(无边框属性)
剩余的UI美化请自行搜索,按照自己喜好来就行,重点是功能实现
1.2 文件界面设计
1、点击 file.ui 进入文件界面
(1)添加一个列表框(QListWidget),命名为file_LW
(2)添加10个按钮(QListWidget),根据按钮功能进行命名
2、UI设计不做详细讲述
1.3 首页设计
1、点击 index.ui 进入首页界面
(1)添加两个按钮(QListWidget),分别是好友和文件,用来切换界面
(2)添加一个窗口控件(QStackedWidget),用来展示不同的界面
2、将好友界面(friend.ui)和文件界面(file.ui)绑定在QStackedWidget上
(1)选中QStackedWidget,右键 ---》 提升为
(2)输入类名,将其提升为窗口,
分别将Friend和File添加到QStackedWidget中。
然后改一下两个页面的名字。
1.4 界面跳转
上面我们完成了首页,文件界面、好友界面的UI设计。
我们的本意是,在首页点击好友按钮,QStackedWidget切换到好友界面,点击文件按钮,切换到文件界面。
因此我们需要在 两个按钮的槽函数中,设置界面跳转。(通过按钮转到槽函数)
1、好友按钮的槽函数
// 首页点击好友按钮,切换到好友页面
void Index::on_friend_PB_clicked()
{
// 设置切换界面--好友界面
ui->stackedWidget->setCurrentIndex(0);
// 设置按钮样式
ui->friend_PB->setStyleSheet("QPushButton{padding:25px;background-color: rgb(255, 255, 255);}");
ui->file_PB->setStyleSheet("QPushButton{padding:25px;background-color: rgba(255, 255, 255,0);}");
}
2、文化按钮的槽函数
// 首页点击文件按钮,切换到文件页面
void Index::on_file_PB_clicked()
{
// 设置切换的界面--文件界面
ui->stackedWidget->setCurrentIndex(1);
// 设置按钮的样式
ui->file_PB->setStyleSheet("QPushButton{padding:25px;background-color: rgb(255, 255, 255);}");
ui->friend_PB->setStyleSheet("QPushButton{padding:25px;background-color: rgba(255, 255, 255,0);}");
}
1.5 实现单例模式
1、实现单例模式
我们在登录界面Client中,将Client类的对象实现了单例模式,目的是为了每个客户端只能实例化一个登录界面。
而每个客户端也只能展示一个首页,因此也要实现单例模式。
(1)构造函数私有化、删除拷贝构造和赋值运算符重载。
index.h
private:
explicit Index(QWidget *parent = nullptr);
Index(const Index& instance) = delete;
Index operator=(const Index&) = delete;
(2)定义静态的成员函数getInstance(),返回一个静态引用的对象。
Index &Index::getInstance()
{
static Index instance;
return instance;
}
2、定义获取好友界面和文件界面的公有函数
我们将Index类实现单例模式之后,就没有办法获取到好友界面和文件界面中的一些控件了,因此我们需要在lndex中,添加获取好友界面和文件界面的公有函数。这样就可以通过这个公有函数来获取某个界面中的控件。
index.cpp
// 获取好友界面
Friend* Index::getFriend()
{
return ui->friendPage;
}
// 获取文件界面
File *Index::getFile()
{
return ui->filePage;
}
1.6 登录后跳转到首页
Day3中,我们完成了登录注册的功能,在登录成功后,我们就需要隐藏登录界面,并跳转到首页。
因此,我们需要在接收消息的函数中,在接收到成功登录的响应之后,将登录界面隐藏,并显示首页。
client.cpp Client::recvMsg()
// 登录响应
case ENUM_MSG_TYPE_LOGIN_RESPOND:
{
// 将消息取出
bool ret;
char caName[32] = {'\0'};
memcpy(&ret,pdu->caData,sizeof(bool));
memcpy(caName,pdu->caData+32,32);
// 根据返回的响应进行处理
if(ret)
{
// 登录成功,用成员变量记录登录成功的用户名
m_LoginName = caName;
qDebug()<<"recvMsg LOGIN_REQUEST m_userName"<<m_LoginName;
QMessageBox::information(this,"登录","登录成功");
// 登录成功后,跳转到首页
Index::getInstance().show();
// 隐藏登录界面
hide();
}
else
{ // 登录失败,提示登录失败
QMessageBox::information(this,"登录","登录失败:用户名或密码非法");
}
break;
}
二、查找用户
通过上面的内容,我们就完成了首页、好友界面、文件界面的设计,以及登录后的界面跳转。接下来,我们就开始正式的完成功能了。下面要完成的是好友界面的查找用户功能。
2.1 查找用户的流程
下面我们来分析一下,查找用户具体有那些流程?
在好友界面点击查找用户的按钮,弹出一个输入框,输入要查找的用户名,将用户名发送给服务器,服务器在用户信息表中查找并返回结果。
- 客户端点击查找用户,发送查找请求(将输入框的用户名发送给服务器)
- 用户点击查找用户,弹出输入框,输入要查找的用户名
- 从输入框中取出用户名
- 构建自定义消息结构体对象pdu(不使用柔性数组(caMsg),使用固定长度的数组(caData[64]))因此在初始化PDU时,传入0即可。
- 在PDU协议中的添加对应功能的请求和响应消息类型
- 在消息类型的枚举值中,添加查找用户的请求和响应
- 在pdu填入消息类型和要发送的数据(用户名)
- 发送 pdu到服务器
- 服务器接收、处理并响应查找用户请求(在用户表中查找用户名)
- 需要数据库操作,在数据库操作类中,添加处理查找用户的函数
- 根据用户名判断用户是否已存在(利用select查找用户名)
- 用户存在,返回查找成功;用户不存在,返回不存在
- 在服务器的接收消息的函数中,处理消息类型为查找用户请求的情况。
- 取出 pdu 中的数据,调用处理函数,将取出的数据传给处理数据库操作类中处理查找用户的函数。
- 得到处理查找用户的函数的返回值,得到处理结果
- 构建发给客户端的响应 pdu,消息类型为 查找用户响应
- 将处理结果放入响应 pdu。
- 将响应 pdu 发送给客户端。
- 需要数据库操作,在数据库操作类中,添加处理查找用户的函数
- 客户端接收查找用户响应并显示结果
- 客户端的接收消息函数中,根据消息类型进行处理,进入查找用户响应的处理阶段
- 将结果显示给用户
2.2 客户端发送查找用户请求
- 客户端点击查找用户,发送查找请求(将输入框的用户名发送给服务器)
- 用户点击查找用户,弹出输入框,输入要查找的用户名
- 从输入框中取出用户名
- 构建自定义消息结构体对象pdu(不使用柔性数组(caMsg),使用固定长度的数组(caData[64]))因此在初始化PDU时,传入0即可。
- 在PDU协议中的添加对应功能的请求和响应消息类型
- 在消息类型的枚举值中,添加查找用户的请求和响应
- 在pdu填入消息类型和要发送的数据(用户名)
- 发送 pdu到服务器
fiend.cpp ---- Friend::on_findUser_PB_clicked()
1、在friend.ui界面中,选择查找用户的按钮,跳转到槽函数
2、在槽函数中,弹出一个输入框(QInputDialog),输入框的返回值就是要查找的用户名
// 输入框 获取 要查找的用户名
QString strName = QInputDialog::getText(this,"查找用户","用户名:");
3、检查输入内容的合法性
if(strName.isEmpty())
{ // 判断输入内容是否为空
QMessageBox::critical(this,"输入错误","用户名不能为空");
return;
}
if(strName.toStdString().size() > 32)
{ // 判断输入内容长度
QMessageBox::critical(this,"输入错误","用户名长度应小于32位");
return;
}
if(strName==Client::getInstance().getLoginName())
{ // 判断要查找的用户是否是自己
QMessageBox::critical(this,"提示","查找的竟然是你自己!");
return;
}
4、构建消息pdu,传入要查找的用户名,并发送给服务器
需要在消息协议的消息类型枚举值中,添加两个枚举值,分别是查找用户的请求和查找用户的响应。
protocol.h
ENUM_MSG_TYPE_FIND_USER_REQUEST, // 查找用户的请求
ENUM_MSG_TYPE_FIND_USER_RESPOND, // 查找用户的响应
fiend.cpp ---- Friend::on_findUser_PB_clicked()
// 构建PDU对象
PDU* pdu = initPDU(0);
// 传入消息类型
pdu->uiMsgType = ENUM_MSG_TYPE_FIND_USER_REQUEST;
// 存放用户名
memcpy(pdu->caData,strName.toStdString().c_str(),strName.toStdString().size());
// 发送消息,应该是socket对象调用write函数
Client::getInstance().getTcpSocket().write((char*)pdu,pdu->uiPDULen);
// 释放pdu
free(pdu);
pdu = NULL;
2.3 服务器处理查找用户请求
- 服务器接收、处理并响应查找用户请求(在用户表中查找用户名)
- 需要数据库操作,在数据库操作类中,添加处理查找用户的函数
- 根据用户名判断用户是否已存在(利用select查找用户名)
- 用户存在,返回查找成功;用户不存在,返回不存在
- 在服务器的接收消息的函数中,处理消息类型为查找用户请求的情况。
- 取出 pdu 中的数据,调用处理函数,将取出的数据传给处理数据库操作类中处理查找用户的函数。
- 得到处理查找用户的函数的返回值,得到处理结果
- 构建发给客户端的响应 pdu,消息类型为 查找用户响应
- 将处理结果放入响应 pdu。
- 将响应 pdu 发送给客户端。
- 需要数据库操作,在数据库操作类中,添加处理查找用户的函数
1、在数据库操作类中,添加处理查找用户的函数
(1)在数据库操作类中定义处理查找用户的函数,需要接收一个参数,就是要查找的用户名
int handleFindUser(const char *name)
(2)利用sql语句,在用户信息表中,查找用户名,并输出在线状态
select online from user_info where name = 'name'
(3)返回查找结果,查找过程出错返回-2,没找到该用户,返回-1,找到了,返回在线状态
operatedb.cpp
// 处理查找用户的函数
int OperateDB::handleFindUser(const char *name)
{
if(name==NULL)
{
return -2; // 空指针 返回-2
}
// 判断用户是否存在,输出在线状态
QString sql = QString("select online from user_info where name = '%1'").arg(name);
// 创建一个 QSqlQuery 对象,用来执行 sql语句
QSqlQuery q;
if(!q.exec(sql))
{
return -2; // 执行错误,返回-2
}
if(q.next())
{ //查找到内容,返回在线状态,需要获取的值为字符串类型,得转换为整型
return q.value(0).toInt();
}
else
{
// 没查到内容,用户不存在
return -1; // 用户不存在
}
}
2、 在服务器接收消息的函数中,处理消息类型为查找用户请求的情况
mytcpsocket.cpp -- MyTcpSocket::recvMsg()
case ENUM_MSG_TYPE_FIND_USER_REQUEST:
(1)将要查找的用户名从pdu中取出,调用数据库操作类中,处理查找用户的函数,得到返回值。
// 将消息取出
char caName[32] = {'\0'};
memcpy(caName,pdu->caData,32);
// 测试
qDebug()<<"recvMsg FIND_USER caName: "<<caName;
// 处理消息
int ret = OperateDB::getInstance().handleFindUser(caName);
(2)构建响应pdu,将得到的返回值存到响应pdu中,并发送到客户端
// 向客户端发送响应
// 初始化响应注册的PDU对象
PDU* findUserPdu = initPDU(0);
// 消息类型为注册响应
findUserPdu->uiMsgType = ENUM_MSG_TYPE_FIND_USER_RESPOND;
// 将消息存储到消息结构体
memcpy(findUserPdu->caData,&ret,sizeof(int));
// 利用socket 向客户端发送 注册的响应
write((char*)findUserPdu,findUserPdu->uiPDULen);
2.4 客户端接收查找用户响应
在客户端接收消息的函数中,根据消息类型,处理消息类型为查找用户响应的情况。
1、将消息从pdu中取出
// 将消息取出
int ret;
memcpy(&ret,pdu->caData,sizeof(int));
2、根据返回值,作出不同的响应
(1)返回值为 -2:查找时出现内部错误
(2)返回值为 -1:没查到该用户
(3)返回值为 0 :该用户不在线
(4)返回值为 1 :该用户在线
// 根据返回的响应进行处理
if(ret == -1)
{
QMessageBox::information(&Index::getInstance(),"查找用户","该用户不存在");
return;
}
else if(ret == 0)
{
QMessageBox::information(&Index::getInstance(),"查找用户","该用户不在线");
return;
}
else if(ret == 1)
{
QMessageBox::information(&Index::getInstance(),"查找用户","该用户在线");
return;
}
else
{
QMessageBox::critical(&Index::getInstance(),"查找用户","查找错误");
return;
}
2.5 功能展示
到这里,我们就完成了查找用户的功能。
当然,你也可以在服务器发回响应时,返回要查找的用户名,在客户端根据不同的响应作出提示的时候,显示你查找的用户名。
三、显示在线用户
3.1 显示在线用户的流程
下面我们来分析一下,显示在线用户具体有那些流程?
在好友界面点击查找用户的按钮,客户端向服务器发送在线用户请求,服务器查找在线的用户,并将结果返回。客户端显示结果。
需要新建一个界面来显示所有的在线用户。
- 客户端点击查找用户,发送在线用户请求(发送请求给服务器)
- UI界面:创建一个设计师界面类(OnlineUser),界面中添加一个 列表框(QListWidget)控件来展示所有的在线用户。
- 用户点击在线用户按钮,发送显示在线用户的请求。
- 构建自定义消息结构体对象pdu,不用传输实际消息,只需要传输请求的类型,因此传入0即可。
- 在PDU协议中的添加对应功能的请求和响应消息类型
- 在消息类型的枚举值中,添加在线用户的请求和响应
- 在pdu填入消息类型,且没有要发送的数据。
- 发送 pdu到服务器
- 服务器接收、处理并响应在线用户请求(在用户表中查找在线用户)
- 需要数据库操作,在数据库操作类中,添加处理在线用户的函数
- 根据online字段,获取在线用户的用户名
- 用户在线,将所有在线的用户名存储在QStringList列表中,并返回字符串列表
- 在服务器的接收消息的函数中,处理消息类型为在线用户请求的情况。
- 调用得到数据库操作类中处理在线用户的函数,得到的返回值,QStringList
- 构建发给客户端的响应 pdu,消息类型为 在线用户响应
- 根据所有在线用户的用户名列表(QStringList)获取用户名的个数
- 根据个数计算出caMsgLen(实际消息的长度) = 个数 ✖ 32
- 遍历QStringList,挨个将在线用户的用户名放入响应 pdu。
- 将响应 pdu 发送给客户端。
- 需要数据库操作,在数据库操作类中,添加处理在线用户的函数
- 客户端接收查找用户响应并显示结果
- 在OnlineUser中,定义一个显示在线用户的函数(showOnlineUser()),将QStringList中的用户名放在 列表框(QListWidget)中。
- 客户端的接收消息函数中,根据消息类型进行处理,进入在线用户响应的处理阶段
- 将pdu中的用户名挨个取出,存储在QStringList中(不包括自己),并调用showOnlineUser()函数传入QStringList
3.2 UI设计
1、添加一个设计师界面类OnlineUser(参考 第一章)
UI界面:创建一个设计师界面类(OnlineUser),界面中添加一个 列表框(QListWidget)控件来展示所有的在线用户。
添加完成后,点击onlineuser.ui,进入界面,添加QListWidget控件,并修改控件的名字。
2、在OnlineUser类的构造函数中,添加关闭选项
在onlineuser的构造函数中,添加setAttribute(Qt::WA_QuitOnClose,false);
目的是为了在主窗口关闭时,该窗口也跟着关闭。
onlineuser.cpp
OnlineUser::OnlineUser(QWidget *parent) :
QWidget(parent),
ui(new Ui::OnlineUser)
{
ui->setupUi(this);
// 主窗口关闭,在线用户窗口也会跟着关闭
setAttribute(Qt::WA_QuitOnClose,false);
}
3、在friend类中,添加一个 OnlineUser类的成员变量
创建该成员变量的目的是,为了调用OnlineUser中的控件以及调用属于OnlineUser类的成员函数。
在friend的构造函数中,new一个新的OnlineUser,赋值给该成员变量。
在friend的析构函数中,释放该成员变量。
friend.cpp
Friend::Friend(QWidget *parent) :
QWidget(parent),
ui(new Ui::Friend)
{
ui->setupUi(this);
// 新建一个 OnlineUser 对象,用于展示在线用户的界面
m_onlineUser = new OnlineUser;
}
Friend::~Friend()
{
if(!m_onlineUser) delete m_onlineUser;
delete ui;
}
3.3 客户端发送在线用户请求
- 客户端点击查找用户,发送在线用户请求(发送请求给服务器)
- 用户点击在线用户按钮,发送显示在线用户的请求。
- 构建自定义消息结构体对象pdu,不用传输实际消息,只需要传输请求的类型,因此传入0即可。
- 在PDU协议中的添加对应功能的请求和响应消息类型
- 在消息类型的枚举值中,添加在线用户的请求和响应
- 在pdu填入消息类型,且没有要发送的数据。
- 发送 pdu到服务器
1、在线用户按钮的点击槽函数中,发送在线用户请求
friend.cpp --- Friend::on_onlineUser_PB_clicked()
(1)如果在线用户界面(onlineuser)是隐藏的,则进行显示
// 如果该界面是隐藏的,则进行展示
if(m_onlineUser->isHidden())
{
m_onlineUser->show();
}
(2)在消息协议的消息类型枚举值中,添加在线用户请求和响应
ENUM_MSG_TYPE_ONLINE_USER_REQUEST, // 在线用户的请求
ENUM_MSG_TYPE_ONLINE_USER_RESPOND, // 在线用户的响应
(3)构建pdu并发送给服务器
// 构建pdu
PDU* pdu = initPDU(0);
// 传入消息类型
pdu->uiMsgType = ENUM_MSG_TYPE_ONLINE_USER_REQUEST;
// 发送pdu到服务器
Client::getInstance().getTcpSocket().write((char*)pdu,pdu->uiPDULen);
// 释放pdu
free(pdu);
pdu = NULL;
3.4 服务器处理在线用户请求
- 服务器接收、处理并响应在线用户请求(在用户表中查找在线用户)
- 需要数据库操作,在数据库操作类中,添加处理在线用户的函数
- 根据online字段,获取在线用户的用户名
- 用户在线,将所有在线的用户名存储在QStringList列表中,并返回字符串列表
- 在服务器的接收消息的函数中,处理消息类型为在线用户请求的情况。
- 调用得到数据库操作类中处理在线用户的函数,得到的返回值,QStringList
- 构建发给客户端的响应 pdu,消息类型为 在线用户响应
- 根据所有在线用户的用户名列表(QStringList)获取用户名的个数
- 根据个数计算出caMsgLen(实际消息的长度) = 个数 ✖ 32
- 遍历QStringList,挨个将在线用户的用户名放入响应 pdu。
- 将响应 pdu 发送给客户端。
- 需要数据库操作,在数据库操作类中,添加处理在线用户的函数
1、在数据库操作类中,定义处理在线用户的函数 (handleOnlineUser)
(1)在用户信息表中,根据online字段,得到所有在线用户的用户名
select name from user_info where online = 1
(2)将所有在线的用户名挨个取出,存储到QStringList列表中,并返回该列表
operatedb.cpp
// 处理在线用户的函数
QStringList OperateDB::handleOnlineUser()
{
// 判断用户是否在线,输出用户名
QString sql = QString("select name from user_info where online = 1");
// 创建一个 QSqlQuery 对象,用来执行 sql语句
QSqlQuery q;
q.exec(sql);
QStringList nameList;
while(q.next())
{
// 测试--打印从数据库取到的每一条用户名
// qDebug()<<"handleOnlineUser "<<q.value(0).toString();
// 将在线的用户名添加到列表中
nameList.append(q.value(0).toString());
}
return nameList;
}
2、在服务器接收消息的函数中,处理消息类型为在线用户请求的情况
(1)调用数据库操作类中,处理查找用户的函数,得到返回值QStringList(所有在线用户的用户名)
// 处理消息
QStringList nameList = OperateDB::getInstance().handleOnlineUser();
(2)计算caMsgLen的长度。
实际消息的长度 = 列表元素个数 ✖ 32(每个用户名的最大长度)
// 获取列表大小
int listSize = nameList.size();
// 计算实际消息的长度
uint uiMsgLen = listSize*32;
(3)构建响应pdu,将在线用户的用户名挨个存储在响应pdu中
// 初始化响应在线用户的PDU对象
PDU* onlineUserPdu = initPDU(uiMsgLen);
// 消息类型为在线用户响应
onlineUserPdu->uiMsgType = ENUM_MSG_TYPE_ONLINE_USER_RESPOND;
// 将用户名 挨个放到 caMsg中
for(int i = 0; i<listSize; i++)
{
// 测试
// qDebug()<<"ONLINE_USER_REQUEST " << nameList.at(i);
// 将每一个用户名都存储到 caMsg中
memcpy(onlineUserPdu->caMsg+i*32,nameList.at(i).toStdString().c_str(),32);
}
(4)将响应pdu 发送给客户端
// 利用socket 向客户端发送 在线用户的响应
write((char*)onlineUserPdu,onlineUserPdu->uiPDULen);
// 释放 onlineUserPdu
free(onlineUserPdu);
onlineUserPdu=NULL;
3.5 客户端接收在线用户响应
- 客户端接收查找用户响应并显示结果
- 在OnlineUser中,定义一个显示在线用户的函数(showOnlineUser()),将QStringList中的用户名放在 列表框(QListWidget)中。
- 客户端的接收消息函数中,根据消息类型进行处理,进入在线用户响应的处理阶段
- 将pdu中的用户名挨个取出,存储在QStringList中(不包括自己),并调用showOnlineUser()函数传入QStringList
1、在OnlineUser类中,定义一个显示在线用户的函数(showOnlineUser())
传入一个QStringList 字符串列表,将该列表中的每一个元素挨个显示到列表框中
onlineuser.cpp
// 展示在线用户
void OnlineUser::showOnlineUser(QStringList nameList)
{
ui->onlineUser_LW->clear();
ui->onlineUser_LW->addItems(nameList);
}
2、在接收消息函数中,根据消息类型,处理消息类型为在线用户响应的情况
(1)挨个取出响应pdu中的用户名,并存放在一个QStringList 中。
先通过实际消息长度,计算用户的个数
再通过循环,挨个取出用户名(不包括自己)
// 获取在线用户的个数
uint listSize = pdu->uiMsgLen/32;
qDebug()<<"listSize "<<listSize;
// 创建变量存储在线用户的用户名
char userName[32];
QStringList nameList;
// 将caMsg中的用户名挨个取出,并放到nameList中
for(uint i = 0; i <listSize; i++)
{
// 挨个取出用户名
memcpy(userName,pdu->caMsg+i*32,32);
// 测试
// qDebug()<<"ONLINE_USER_RESPOND "<<QString(userName);
// 跳过自己
if(QString(userName) == m_LoginName)
{
continue;
}
// 将取到的用户名存放到 nameList中
nameList.append(QString(userName));
}
(2)调用OnlineUser中,定义的显示在线用户的函数(showOnlineUser()),并将QStringList 传入
// 调用展示在线用户的函数
Index::getInstance().getFriend()->m_onlineUser->showOnlineUser(nameList);
3.6 功能展示
到这里,我们就完成了在线用户的功能。
当然,你也可以在服务器在数据拷表中查找数据时,就将自己的用户名排除在外,这样在使用socket传输数据时,就可以节省32字节的大小。
这里,登录了4个客户端。
总结
实现的内容
1、完成了首页、好友界面、文件界面的UI界面设计
2、完成了好友界面的添加用户功能、显示在线用户功能
注意:
再次声明本文未经授权,禁止转载
1、对于UI界面的设计,请自行完成美化。
2、对于控件的名称,一定要按照控件的功能进行命名,否则后面调用控件的时候,你自己都不知道这是什么控件。
3、如果你发现了文章中的问题,勿喷!!请私信我,我会改正。(十分感谢)
完整代码
代码量增加,把所有代码复制粘贴到CSDN是不现实的,想要源码的话,就自己去历史提交里找吧。下面这几次提交。