a、资源占用合理,虽然相比较windows自带的GUI框架例如WIn32 SDKs和MFC以及和第三方的windows GUI库比如duilib soui等内存占用稍多,但相比较Electron相同的Ui只需要五分之一甚至十分之一的资源利用即可完成。比起QML和WPF也仅需三分之一的内存即可实现相同效果的UI。当然WPF不能跨平台。程序员口中的性能过剩在企事业单位中不存在,在4G内存的电脑上Electron等方案并不可行。
b、高性能,QWidget完全使用C++开发,因此一如既往的继承了C++的高性能特性,尤其是在低配置的计算机上,优势明显。但是由于QWidget使用CPU绘图,因此在高配置的电脑上除了节省资源,并不能体现出太大的Ui渲染优势。
c、使用C++开发,具备更底层的控制和灵活性。得益于C++的语言优势。Qt可以使用所有的C++第三方库,具备非常强的扩展和更高级的计算机使用权限,于此同时提供更加底层的设备访问功能和灵活的外部程序嵌入。由于使用C++开发,因此可以通过C风格导出风格封装 可作为插件提供用于其他语言跨语言调用。方便系统集成,这是高级语言无法做到的。
d、对计算机配置要求低。得益于C++的内存结构和执行原理,导致C++程序在资源利用上有着先天优势,相同的功能只需要高级语言内存的五分之一到十分之一即可完成。
e、跨平台,在完善的全方位跨平台的GUI框架选择上Qt似乎唯一而非之一,因此不再多说。
f、完善程度高,相比较GTK,Qt可以说是最佳的选择,无论从组件功能还是底层APi封装上都提供了相对完整的体验,没有技术断层。
g、支持高分屏,其实Qt在5之前高分屏支持很不理想,即便是在Qt中 5.14.2之前的版本都存在问题。在5.12之前只支持整倍缩放,在5.14.2时虽然支持了非整倍缩放,但是存在非整倍缩放时控件底部会存在一条绝对一像素的穿透。
第一篇文章,前奏有点长,现在开始正文。
a.支持分组显示或不分组显示。
b.支持自定义分组和索引导航分组
c.支持头像的圆角比例
d.支持灰度头像
e.支持标头状态线
f.支持分割线
g.支持各种自定义颜色
h.支持使用文字头像或图像头像或图像不存在时是用文字头像
i.支持多种排序模式
j.支持vip和svip标识
k.支持列表和图标模式
l.支持自定义所有图标
m.支持选种样式风格
n.支持好友分组成员统计
o.支持鼠标单击,双击,悬停时触发Ui元素事件
p.支持右击时发送分组数据或好友数据信号
q.支持元素悬停效果
r.支持自定义选项按钮
s.支持鼠标移动上去之后显示选项按钮,包括自定义选项,语音通话,视频通话 等功能
gif模糊的没法看,那就一张一张来
图不多上,由于篇幅有限我们首先重点说明计数要点
1、分组,由于是ListView,因此主要靠Mode的setData的role区分分组和用户,分组展开和闭合是通过设置行隐藏和显示实现,在目标分组下存在一个与分组相同的data role,根据此role隐藏或显示row。由于这个实现没有自定义处理mode,因此在数据多的时候并不理想,但这不是重点。因此此列表视图建议好友数量不要超过一万。否则会影响体验。笔者测试数据为3万条
2、item元素事件,根据Rect区域判断item上的图标或按钮区域,所有按钮皆通过绘制模拟,并非button等类。
重要代码:
1、样式风格结构体
/// 会话列表项风格委托样式数据结构
///
typedef struct LQFRIENDSLIST_STYLE_{
QColor cGroupBkgColor = QColor(88,88,88,44); //分组背景色
QColor cGroupExpanClr = QColor(77,77,77,44); //分组展开背景色
QColor cGroupHoverClr = QColor(254,118,19); //分组鼠标悬停背景颜色
QColor cGroupExpHover = QColor(238,156,109); //分组展开悬停背景色
QColor cGroupsBorders = QColor(117,131,218); //分组边框颜色
QColor cItemsBkgColor = QColor(192,192,192,22);//好友背景色
QColor cItemsHoverClr = QColor(69,176,142); //好友鼠标悬停背景色
QColor cItemSelectClr = QColor(244,123,64); //好友选中背景色
QColor cIconHoverClrs = QColor(22,22,22,44); //鼠标悬停颜色
QColor cIconSelectClr = QColor(11,11,11,44); //选中项时颜色
QColor cItemNamesClrs = QColor(33,33,33); //好友名称文字颜色
QColor cNameHoverClrs = QColor(220,220,220); //好友名称文字鼠标悬停时颜色
QColor cNameSelectClr = QColor(255,255,255); //好友名称文字选中时颜色
QColor cGroupTxtColor = QColor(44,44,44); //分组背景色
QColor cGroupHoverTxt = QColor(255,255,255); //分组鼠标悬停背景颜色
QColor cGroupExpanTxt = QColor(33,33,33); //分组鼠标悬停背景颜色
QColor cItemsTxtColor = QColor(33,33,33); //好友文字颜色
QColor cItemsHoverTxt = QColor(255,255,255); //好友鼠标悬停背景色
QColor cItemSelectTxt = QColor(235,235,235); //好友选中背景色
QColor cGroupCountBkg = QColor(127,127,127); //分组成员统计背景颜色
QColor cGroupCountTxt = QColor(220,220,220); //分组成员统计文字颜色
QColor cGroupNodeClrs = QColor(127,127,127); //分组节点图标颜色
QColor cGroupNodeBkgd = QColor(58,191,136); //分组节点背景颜色
QColor cGroupNodesExp = QColor(114,133,218); //分组节点展开背景颜色
QColor cDescHoverClrs = QColor(144,144,144); //摘要文字鼠标悬停颜色
QColor cDescSelectClr = QColor(200,200,200); //摘要文字选中颜色
QColor cItemHeadLines = QColor(63,166,148,160); //头部参考线颜色
QColor cItemSplitLine = QColor(63,166,148,66); //分割线颜色
QColor cAvatarFrmClrs = QColor(250,74,33); //头像边框颜色
QColor cMouseHoverBkg = QColor(124,144,218); //鼠标悬停背景颜色
QColor cAvatarBkgClrs = QColor(55,55,55); //头像背景填充颜色
QColor cUsersNodesClr = QColor(145,162,229); //头像节点颜色
QColor cVipIconBkgClr = QColor(228,200,200,180); //会员图标背景颜色
QColor cVipNameColors = QColor(251,50,44); //会员名称颜色
QColor cVipDescColors = QColor(235,155,51); //会员消息颜色
QColor cVipNameHovers = QColor(252,75,40); //会员名称颜色
QColor cVipDescHovers = QColor(250,116,42); //会员消息颜色
QColor cVipNameSelect = QColor(240,240,240); //会员名称颜色
QColor cVipDescSelect = QColor(250,250,250); //会员消息颜色
QColor cDeviceBkgClrs = QColor(127,156,230); //设备背景颜色
QColor cOptionBkgClrs = QColor(248,148,72); //选项按钮背景颜色
QColor cVideosBkgClrs = QColor(130,144,228); //选项按钮悬停颜色
QColor cAudiosBkgClrs = QColor(71,186,152); //选项按钮悬停颜色
QColor cOptionHoveClr = QColor(248,74,99); //选项按钮悬停颜色
bool bRoundAvtiveBg = true; //选中背景是否绘制成半圆
bool bShowHeadLines = true; //是否显示头部线条
bool bShowSplitLine = true; //是否显示分割线
bool bShowDeviceInf = true; //是否显示设备信息
uint nSplitLineType = 2; //分割线样式,0实线,1虚线,2点线,3点划线,4双点划线
uint nHeadeLineType = 2; //标头参考线样式,0实线,1虚线,2点线,3点划线,4双点划线
bool bIconViewModel = false; //是否为列表视图,默认是
uint uItemHeightVal = 50; //行高
uint uItemMinHeight = 34; //小图标模式行高
uint uGroupsHeights = 26; //分组行高
uint uVipShowStyles = 1; //VIP显示风格,0不显示,1显示等级,2显示logo
bool uShowStatusIco = true; //是否在头像处显示联机状态
uint uHeadRectWidth = 40; //头部宽度
uint uIconGridWidth = 72; //ICON视图格子宽度
uint uIconGridHeigh = 100; //ICON视图格子高度
uint uGroupStyleIds = 0; //分组风格,0默认风格,1居中
uint uGroupBorderId = 0; //分组边框风格,0不显示边框,1全部边框,2上,3下
float fBordersWidths = 0.5; //边框宽度
double dGroupRadiusId = 0; //分组边框半径
uint uItemsStyleIds = 0; //列表项风格,0大图标模式,1小图标模式
uint uExpandGroupId = 1; //展开和闭合分组操作方式,0单击箭头,1单击整个分组项目,0双击简单,2双击整个分组项
uint uHorizeMarginG = 0; //分组项水平Margin
uint uVerticMarginG = 0; //分组项垂直Margin
uint uHorizeMarginU = 0; //用户项水平Margin
uint uVerticMarginU = 0; //用户项垂直Margin
double dAvatarsRadius = 0.5; //用户头像圆角半径
bool bShowUserCount = true; //是否显示分组成员统计
bool bIsShowSummary = true; //显示用户摘要,如果为false则表示显示来源
bool bShowOptionBtn = true; //显示选项按钮
bool bIsShowMoreBtn = true; //是否显示自定义处理按钮
bool bRandomAvbgClr = true; //在显示文字头像事是否使用随机背景颜色
uint bAvatarsStyles = 1; //头像显示风格索引,0仅显示图像,无头像图像时显示默认头像,1无图像时显示名称第一字符,2仅显示名称第一字符
bool bGrayAvatarImg = true; //是否启用头像灰度,启用后如果用户脱机则显示灰色头像
bool operator==(const LQFRIENDSLIST_STYLE_& rhs) // 操作运算符重载
{
//省略篇幅已删减
}
bool operator != (const LQFRIENDSLIST_STYLE_& rhs) // !=操作运算符重载
{
return !(*this == rhs);
}
}LQFRIENDSLIST_STYLE,*PLQFRIENDSLIST_STYLE;
2、委托绘制类头文件部分代码
class LNCF_QTSOCIALLIBS_API Lncf_QFriendsItemd:public QStyledItemDelegate
{
Q_OBJECT
public:
explicit Lncf_QFriendsItemd(QObject *parent = nullptr);
~Lncf_QFriendsItemd();
private:
/// 初始化好友列表委托
/// \brief InitFriendsItemd
///
void InitFriendsItemd();
protected:
/// 好友列表项风格委托样式数据结构变量
/// \brief tDelegateStyle
///
LQFRIENDSLIST_STYLE tDelegateStyle;
QString sDevicePcIcons; //PC客户端图标
QString sDeviceMbIcons; //手机客户端图标
QString sDeviceWbIcons; //web客户端图标
QString sOtherAppIcons; //其它应用图标
QString sVipLogoImages; //会员图标
QString sSvipLogoImage; //超级会员图标
QString sOnlineImgLogo; //在线状态图标
QString sOffLineImages; //离线状态图标
QString sLeaveLogosImg; //离开状态图标
QString sBusyStatusImg; //忙碌状态图标
QString sStealthImages; //隐身状态图标
QString sNotDisturbImg; //勿扰状态图标
QString sCustomizeImgs; //自定义状态图标
QString sCallMeStatImg; //与我联系状态图标
QString sSourceAppsImg; //默认来源应用图标
QString sGroupExpanImg; //分组展开图标
QString sGroupCloseImg; //分组关闭图标
QString sVideoChatImgs; //视频会话图标
QString sAudioChatImgs; //音频通话图标
QString sMoreOptionImg; //更多选项图标
QStringList sVipLogoImgLst; //会员等级图标列表
QStringList sSvipLogosList; //超级会员等级图标列表
QImage pGroupExpanImg; //分组展开图标对象
QImage pGroupCloseImg; //分组收起图标对象
bool bHasMouseEvent; //是否启用鼠标事件
bool bHasHoverEvent = false; //是否处理鼠标悬停事件
bool bShowItemsTips = true; //是否显示鼠标悬停时提示信息
bool bNavGroupStyle = false; //是否为索引分组风格
protected:
/// 重写创建编辑器
QWidget *createEditor(QWidget *parent, const QStyleOptionViewItem &option,const QModelIndex &index) const override;
/// 重写系统绘制
virtual void paint(QPainter *painter, const QStyleOptionViewItem &option, const QModelIndex &index) const Q_DECL_OVERRIDE;
/// 绘制背景
/// \brief DrawBackgrounds
/// \param painter
/// \param option
/// \param bGroup
/// \param bExpand
///
virtual void DrawBackgrounds(QPainter *painter,const QStyleOptionViewItem &option,bool bGroup,bool bExpand) const;
/// 绘制分组项
/// \brief DrawItemsGroups
/// \param painter
/// \param option
/// \param itemData
///
virtual void DrawItemsGroups(QPainter *painter,const QStyleOptionViewItem &option,bool bExpand,LNCFQFRIENDS_LISTGROUP itemData,const QModelIndex &index) const;
/// 绘制头部参考线
/// \brief DrawItemsHeader
/// \param painter
/// \param option
/// \param bGroup
/// \param uStatus
///
virtual void DrawItemsHeader(QPainter *painter,const QStyleOptionViewItem &option,bool bGroup,uint uStatus) const;
/// 绘制头像
/// \brief DrawItemsAavtar
/// \param painter
/// \param option
/// \param sImagePath
/// \param bHover
/// \param uSex
/// \param uStatus
/// \param uDevice
/// \param sName
///
virtual void DrawItemsAavtar(QPainter *painter,const QStyleOptionViewItem &option,std::u16string sImagePath,bool bHover,uint uSex,uint uStatus,std::u16string sName=u"") const;
/// 绘制会员等级图标
/// \brief DrawVipLevelImg
/// \param painter
/// \param option
/// \param bSvip
/// \param nVipLevel
/// \param bHover
/// \param sImage
///
virtual void DrawVipLevelImg(QPainter *painter,const QStyleOptionViewItem &option,bool bSvip,int nVipLevel,bool bHover,std::u16string sImage) const;
/// 绘制好友显示名
/// \brief DrawDisplayName
/// \param painter
/// \param option
/// \param sName
///
virtual void DrawDisplayName(QPainter *painter,const QStyleOptionViewItem &option,std::u16string sName,bool bVips) const;
/// 绘制用户摘要信息
/// \brief DrawUserSummary
/// \param painter
/// \param option
/// \param sDesc
/// \param bVips
///
virtual void DrawUserSummary(QPainter *painter,const QStyleOptionViewItem &option,std::u16string sDesc,bool bVips) const;
/// 绘制来源信息
/// \brief DrawSourcesInfo
/// \param painter
/// \param option
/// \param sName
/// \param sImage
///
virtual void DrawSourcesInfo(QPainter *painter,const QStyleOptionViewItem &option,std::u16string sName,std::u16string sImage,bool bVips) const;
/// 绘制设备图标
/// \brief DrawDeviceImage
/// \param painter
/// \param option
/// \param nDeviceType
/// \param bHover
///
virtual void DrawDeviceImage(QPainter *painter,const QStyleOptionViewItem &option,int nDeviceType,bool bHover) const;
/// 绘制选项图标
/// \brief DrawOptionsBtns
/// \param painter
/// \param option
/// \param uHover :鼠标移动的控件索引,0无,1音频通话,2视频通话,3更多
/// \param bAudio :是否绘制音频通话快速按钮
/// \param bVideo :是否绘制视频通话快速按钮
///
virtual void DrawOptionsBtns(QPainter *painter,const QStyleOptionViewItem &option,uint uHover,bool bAudio,bool bVideo) const;
/// 绘制分割线
/// \brief DrawSplitupLine
/// \param painter
/// \param option
///
virtual void DrawSplitupLine(QPainter *painter,const QStyleOptionViewItem &option) const;
public:
/// 获取视图项目风格数据结构
/// \brief GetItemStyleData
/// \return
///
LQFRIENDSLIST_STYLE GetItemStyleData() const;
/*属性接口省略*/
protected:
/// 获取默认头像路径
/// \brief GetDefaultAvatar
/// \param uSex
/// \return
///
QString GetDefaultAvatar(uint uSex) const;
/// 获取元素矩形区域
/// \brief GetElementRectan
/// \param option
/// \param index
/// \param bGroup :是否为分组项
/// \param nType,用户项索引“0:头像区域,1设备图标,2vip图标,3视频通话区域,4音频通话区域,5更多选项区域,6名称区域,7摘要区域”,分组项索引0节点图标,1名称,2统计数量
/// \return
///
virtual QRect GetElementRectan(const QStyleOptionViewItem &option,const QModelIndex &index,const bool bGroup,const int nType) const;
/// 重写视图项尺寸定义
virtual QSize sizeHint(const QStyleOptionViewItem &option, const QModelIndex &index) const Q_DECL_OVERRIDE;
/// 重写视图项编辑事件
virtual bool editorEvent(QEvent *event,QAbstractItemModel *model,const QStyleOptionViewItem &option,const QModelIndex &index) override;
/// 重写视图项辅助事件
virtual bool helpEvent(QHelpEvent *event,QAbstractItemView *view,const QStyleOptionViewItem &option,const QModelIndex &index) override;
signals:
/// 列表分组展开或收起
/// \brief GroupItemsExpand
/// \param bExpand 是否展开,true表示展开否则为收起
/// \param uGroup :分组ID
/// \param index :模型索引ID
///
void GroupItemsExpand(const bool &bExpand,const uint64_t uGroup, const QModelIndex &index);
/// 列表视图元素单击事件信号
/// 注意:调用SetHasMouseEvent(true)接口设置鼠标事件之后此信号将被发送
/// \brief ItemsElementEvts
/// \param nElement 元素索引定义[0:头像,1设备图标,2vip图标,3视频通话,4音频通话,5其它选项]
/// \param index
///
void ItemElementClick(const int &nElement,const QModelIndex &index);
/// 列表视图元素双击事件信号
/// 注意:调用SetHasMouseEvent(true)接口设置鼠标事件之后此信号将被发送
/// \brief ItemElementDbClick
/// \param nElement 0:头像,1设备图标,2vip图标,3视频通话,4音频通话,5其它选项
/// \param index
///
void ItemElementDbClick(const int &nElement,const QModelIndex &index);
/// 分组右键单击事件
/// \brief GroupsRightClicked
/// \param tGroupInf
/// \param point
///
void GroupsRightClicked(const LNCFQFRIENDS_LISTGROUP &tGroupInf,const QPoint &point,const QModelIndex &index);
/// 好友右键单击事件
/// \brief UserRightClickEvts
/// \param tUsersInf
/// \param point
///
void UserRightClickEvts(const LNCFQFRIENDS_LISTVIEWS &tUsersInf,const QPoint &point,const QModelIndex &index);
/// 列表视图Ui元素鼠标悬停事件
/// 注意:调用SetHasHoverEvent(true)接口设置悬停事件启用之后此信号将被发送
/// \brief ItemsElementHovers
/// \param nElement 元素索引定义[0:头像,1设备图标,2vip图标,3视频通话,4音频通话,5其它选项]
/// \param index
/// \param itemRect 当前项目矩形区域
/// \param evtPos 鼠标悬停位置坐标[当前窗口]
/// \param evtGlobalPos 鼠标悬停位置坐标[桌面窗口]
///
void ItemsElementHovers(const int &nElement,const QModelIndex &index,const QRect &itemRect,const QPoint &evtPos,const QPoint &evtGlobalPos);
};
5、部分重要绘图部分代码
/// 绘制背景
/// \brief DrawBackgrounds
/// \param painter
/// \param option
/// \param bGroup
///
void Lncf_QFriendsItemd::DrawBackgrounds(QPainter *painter,const QStyleOptionViewItem &option,bool bGroup,bool bExpand) const
{
painter->save();
if(bGroup){
QRect rcGroup;
painter->setBrush(bExpand?this->tDelegateStyle.cGroupExpanClr:this->tDelegateStyle.cGroupBkgColor);
if (option.state.testFlag(QStyle::State_MouseOver))
painter->setBrush(!bExpand?this->tDelegateStyle.cGroupHoverClr:this->tDelegateStyle.cGroupExpHover);
rcGroup = QRect(option.rect.left()+tDelegateStyle.uHorizeMarginG,option.rect.top()+tDelegateStyle.uVerticMarginG,option.rect.width()-tDelegateStyle.uHorizeMarginG*2,option.rect.height()-tDelegateStyle.uVerticMarginG*2);
double dRadius = tDelegateStyle.dGroupRadiusId*rcGroup.height();
painter->drawRoundedRect(rcGroup,dRadius,dRadius);
if(tDelegateStyle.uGroupBorderId>0&&tDelegateStyle.uGroupBorderId<4)
{
painter->setBrush(Qt::NoBrush);
QPen penBorder(tDelegateStyle.cGroupsBorders);
penBorder.setWidthF(tDelegateStyle.fBordersWidths);
painter->setPen(penBorder);
}
//分组边框风格,0不显示边框,1全部边框,2上,3下
switch (tDelegateStyle.uGroupBorderId) {
case 1:{
painter->drawRoundedRect(rcGroup,dRadius,dRadius);
}break;
case 2:{
painter->drawLine(rcGroup.left(),rcGroup.top()-tDelegateStyle.fBordersWidths,rcGroup.right(),rcGroup.top()-tDelegateStyle.fBordersWidths);
}break;
case 3:{
painter->drawLine(rcGroup.left(),rcGroup.bottom()+1,rcGroup.right(),rcGroup.bottom()+1);
}break;
}
}
else{
if(tDelegateStyle.bIconViewModel)
painter->setBrush(Qt::transparent);
else{
painter->setBrush(this->tDelegateStyle.cItemsBkgColor);
painter->drawRect(option.rect);
}
bool bDarw = true;
painter->setPen(Qt::NoPen);
if (option.state.testFlag(QStyle::State_MouseOver))
painter->setBrush(!tDelegateStyle.bIconViewModel?tDelegateStyle.cItemsHoverClr:tDelegateStyle.cIconHoverClrs);
if (option.state.testFlag(QStyle::State_Selected)){
painter->setBrush(!tDelegateStyle.bIconViewModel?tDelegateStyle.cItemSelectClr:tDelegateStyle.cIconSelectClr);
if(tDelegateStyle.bRoundAvtiveBg&&!tDelegateStyle.bIconViewModel){
QRect rcBkg =option.rect;
rcBkg.setLeft(rcBkg.left()+rcBkg.height()/2-1);
QRect rcRnd =QRect(option.rect.left(),option.rect.top(),option.rect.height(),option.rect.height());
painter->drawPie(rcRnd,90.0*16,180.0*16);
painter->drawRect(rcBkg);
bDarw = false;
}
}
if (option.state.testFlag(QStyle::State_Selected)&&option.state.testFlag(QStyle::State_MouseOver))
painter->setBrush(!tDelegateStyle.bIconViewModel?tDelegateStyle.cItemsHoverClr:tDelegateStyle.cIconHoverClrs);
if(!tDelegateStyle.bIconViewModel){
if(bDarw)
painter->drawRect(option.rect);
}
else{
painter->drawRoundedRect(option.rect, 5, 5);
}
}
painter->restore();
}
/// 绘制分组项
/// \brief DrawItemsGroups
/// \param painter
/// \param option
/// \param itemData
///
void Lncf_QFriendsItemd::DrawItemsGroups(QPainter *painter,const QStyleOptionViewItem &option,bool bExpand,LNCFQFRIENDS_LISTGROUP itemData,const QModelIndex &index) const
{
painter->save();
(void)index;
QRectF rcGroup,rcIcon,rcNode,rcText,rcCount;
rcGroup = QRect(option.rect.left()+tDelegateStyle.uHorizeMarginG,option.rect.top()+tDelegateStyle.uVerticMarginG,option.rect.width()-tDelegateStyle.uHorizeMarginG*2,option.rect.height()-tDelegateStyle.uVerticMarginG*2);
rcIcon = QRectF(rcGroup.left()+4,rcGroup.top()+rcGroup.height()/2-7,14,14);
rcText = QRect(rcGroup.left()+25,rcGroup.top(),rcGroup.right()-50,rcGroup.height());
rcNode = QRect(rcGroup.left()+7,rcGroup.top()+rcGroup.height()/2-4,8,8);
QFont fontObj=painter->font();
QFontMetrics fontMetrics(fontObj);
fontObj.setPixelSize(7);
QString sCount = QString("%1").arg(itemData.uGroupMemCount);
int countWidth = fontMetrics.horizontalAdvance(sCount);
if(this->bNavGroupStyle){
rcCount = QRect(rcNode.left()+(this->tDelegateStyle.uGroupStyleIds==1?10:40),rcGroup.top()+rcGroup.height()/2-7,countWidth+7,14);
painter->setPen(Qt::NoPen);
painter->setBrush(bExpand?tDelegateStyle.cGroupNodesExp:tDelegateStyle.cGroupNodeBkgd);
painter->drawEllipse(rcNode);
}
else{
rcCount = QRect(rcGroup.right()-countWidth-11,rcGroup.top()+rcGroup.height()/2-8,countWidth+7,14);
QImage icoNode = bExpand?pGroupExpanImg:pGroupCloseImg;
if(icoNode.isNull())
icoNode = bExpand?QImage(sGroupExpanImg):QImage(sGroupCloseImg);
painter->drawImage(rcIcon, icoNode);
}
painter->setPen(tDelegateStyle.cGroupTxtColor);
if (option.state.testFlag(QStyle::State_MouseOver))
painter->setPen(tDelegateStyle.cGroupHoverTxt);
if(bExpand)
painter->setPen(tDelegateStyle.cGroupExpanTxt);
if(this->tDelegateStyle.uGroupStyleIds==1)
painter->drawText(rcText,Qt::AlignCenter,QString::fromStdU16String(itemData.sItemGroupName));
else
painter->drawText(rcText,Qt::AlignVCenter,QString::fromStdU16String(itemData.sItemGroupName));
if(tDelegateStyle.bShowUserCount){
painter->setPen(Qt::NoPen);
painter->setBrush(tDelegateStyle.cGroupCountBkg);
painter->drawRoundedRect(rcCount,rcCount.height()/2,rcCount.height()/2);
painter->setPen(tDelegateStyle.cGroupCountTxt);
painter->setFont(fontObj);
painter->drawText(rcCount,Qt::AlignCenter,QString("%1").arg(itemData.uGroupMemCount));
}
painter->restore();
}
/// 绘制头部参考线
/// \brief DrawItemsHeader
/// \param painter
/// \param option
/// \param bGroup
///
void Lncf_QFriendsItemd::DrawItemsHeader(QPainter *painter,const QStyleOptionViewItem &option,bool bGroup,uint uStatus) const
{
if(!this->tDelegateStyle.bIconViewModel){
painter->save();
//绘制辅助线
QPen pen(tDelegateStyle.cItemHeadLines);
pen.setWidthF(0.5F);
switch (tDelegateStyle.nHeadeLineType) {
case 0: pen.setStyle(Qt::SolidLine); break;
case 1: pen.setStyle(Qt::DashLine); break;
case 2: pen.setStyle(Qt::DotLine); break;
case 3: pen.setStyle(Qt::DashDotLine); break;
case 4: pen.setStyle(Qt::DashDotDotLine); break;
}
painter->setPen(pen);
painter->setBrush(Qt::NoBrush);
painter->drawLine(QPointF(option.rect.left()+11,option.rect.top()),QPointF(option.rect.left()+11,option.rect.bottom()));
painter->drawLine(QPointF(option.rect.left(),option.rect.top()+option.rect.height()/2+pen.widthF()),QPointF(option.rect.left()+(bGroup?11:19),option.rect.top()+option.rect.height()/2+pen.widthF()));
if(!bGroup)
{
painter->setPen(Qt::NoPen);
painter->setBrush(tDelegateStyle.cUsersNodesClr);
QRectF nodesRect = QRect(option.rect.left()+6, option.rect.top()+option.rect.height()/2-5, 10, 10);
QRectF stateRect = QRect(option.rect.left()+6, option.rect.top()+option.rect.height()/2-5, 10, 10);
if(uStatus>=0&&uStatus<8){
painter->drawEllipse(nodesRect);
///0脱机,1联机,2勿扰,3离开,4忙碌,5隐身,6自定义
switch (uStatus) {
case 0:painter->drawImage(stateRect, QImage(sOffLineImages));break;
case 1:painter->drawImage(stateRect, QImage(sOnlineImgLogo));break;
case 2:painter->drawImage(stateRect, QImage(sNotDisturbImg));break;
case 3:painter->drawImage(stateRect, QImage(sLeaveLogosImg));break;
case 4:painter->drawImage(stateRect, QImage(sBusyStatusImg));break;
case 5:painter->drawImage(stateRect, QImage(sOffLineImages));break;
case 6:painter->drawImage(stateRect, QImage(sCallMeStatImg));break;
case 7:painter->drawImage(stateRect, QImage(sCustomizeImgs));break;
}
}
else{
painter->drawEllipse(stateRect);
}
}
painter->restore();
}
}
/// 绘制头像
/// \brief DrawItemsAavtar
/// \param painter
/// \param option
/// \param sImagePath
/// \param bHover
/// \param uSex
/// \param uStatus
/// \param uDevice
///
void Lncf_QFriendsItemd::DrawItemsAavtar(QPainter *painter,const QStyleOptionViewItem &option,std::u16string sImagePath,bool bHover,uint uSex,uint uStatus,std::u16string sName) const
{
painter->save();
int leftMargin =0;
QRectF avatarRect,borderRect,stateRect,stateFrm;
if(!this->tDelegateStyle.bIconViewModel){
leftMargin=!tDelegateStyle.bShowHeadLines?option.rect.left()+5:option.rect.left()+20;
if(option.rect.height()>41){
avatarRect = QRect(leftMargin, option.rect.top()+5, option.rect.height()-10, option.rect.height()-10);
borderRect = QRect(leftMargin-2, option.rect.top()+3, option.rect.height()-6, option.rect.height()-6);
stateRect = QRect(leftMargin, option.rect.top()+5,12,12);
stateFrm = QRect(leftMargin, option.rect.top()+5,12,12);
}
else{
avatarRect = QRect(leftMargin, option.rect.top()+4, option.rect.height()-8, option.rect.height()-8);
borderRect = QRect(leftMargin-2, option.rect.top()+2, option.rect.height()-4, option.rect.height()-4);
stateRect = QRect(leftMargin, option.rect.top()+4,10,10);
stateFrm = QRect(leftMargin, option.rect.top()+4,10,10);
}
}
else{
borderRect = QRect(option.rect.left()+8, option.rect.top()+8, this->tDelegateStyle.uIconGridWidth-16, this->tDelegateStyle.uIconGridWidth-16);
avatarRect = QRect(option.rect.left()+12, option.rect.top()+12, this->tDelegateStyle.uIconGridWidth-24, this->tDelegateStyle.uIconGridWidth-24);
stateRect = QRect(option.rect.left()+13, option.rect.top()+13,12,12);
stateFrm = QRect(option.rect.left()+13, option.rect.top()+13,12,12);
}
if(tDelegateStyle.bIconViewModel){
painter->setPen(Qt::NoPen);
int nRadius=borderRect.height()*tDelegateStyle.dAvatarsRadius;
if (option.state.testFlag(QStyle::State_MouseOver)) {
painter->setBrush(tDelegateStyle.cItemsHoverClr);
painter->drawRoundedRect(borderRect,nRadius,nRadius);
}
if (option.state.testFlag(QStyle::State_Selected)) {
painter->setBrush(tDelegateStyle.cItemSelectClr);
painter->drawRoundedRect(borderRect,nRadius,nRadius);
}
}
else{
painter->setPen(Qt::NoPen);
int nRadius=borderRect.height()*tDelegateStyle.dAvatarsRadius;
if (option.state.testFlag(QStyle::State_Selected)) {
painter->setBrush(tDelegateStyle.cAvatarFrmClrs);
painter->drawRoundedRect(borderRect,nRadius,nRadius);
}
else{
if(bHover&&this->bHasMouseEvent){
painter->setBrush(tDelegateStyle.cMouseHoverBkg);
painter->drawRoundedRect(borderRect,nRadius,nRadius);
}
else{
painter->setBrush(Qt::transparent);
painter->drawRoundedRect(borderRect,nRadius,nRadius);
}
}
}
if(tDelegateStyle.dAvatarsRadius>0){
QPainterPath pathAvatar;
int nRadius=avatarRect.height()*tDelegateStyle.dAvatarsRadius;
pathAvatar.addRoundedRect(avatarRect , nRadius, nRadius);
painter->setClipPath(pathAvatar);
}
//头像显示风格索引,0仅显示图像,无头像图像时显示默认头像,1无图像时显示名称第一字符,2仅显示名称第一字符
switch (tDelegateStyle.bAvatarsStyles) {
case 1:{
painter->setBrush(tDelegateStyle.bRandomAvbgClr?QColor::fromHsl(rand()%360,rand()%256,rand()%200):tDelegateStyle.cAvatarBkgClrs);
painter->drawRect(avatarRect);
QImage imgAvatar = QImage(QString::fromStdU16String(sImagePath));
if(imgAvatar.isNull()&&!sName.empty()){
painter->setPen(this->tDelegateStyle.cItemsTxtColor);
QFont fontObj=painter->font();
fontObj.setPixelSize(avatarRect.height()/2.5);
painter->setFont(fontObj);
QString szName = QString::fromStdU16String(sName.substr(0,1));
painter->drawText(avatarRect,Qt::AlignCenter,szName);
}
else{
if(imgAvatar.isNull())
imgAvatar = QImage(GetDefaultAvatar(uSex));
if((uStatus==0||uStatus==5)&&tDelegateStyle.bGrayAvatarImg)
imgAvatar = imgAvatar.convertToFormat(QImage::Format_Grayscale8,Qt::AutoColor);
painter->drawImage(avatarRect, imgAvatar);
}
}break;
case 2:{
painter->setBrush(tDelegateStyle.bRandomAvbgClr?QColor::fromHsl(rand()%360,rand()%256,rand()%200):tDelegateStyle.cAvatarBkgClrs);
painter->drawRect(avatarRect);
QString szName = QString::fromStdU16String(sName.substr(0,1));
painter->setPen(this->tDelegateStyle.cItemsTxtColor);
QFont fontObj=painter->font();
fontObj.setPixelSize(avatarRect.height()/2.5);
painter->setFont(fontObj);
painter->drawText(avatarRect,Qt::AlignCenter,szName.isEmpty()?"#":szName);
}break;
default:{
painter->setBrush(tDelegateStyle.cAvatarBkgClrs);
painter->drawRect(avatarRect);
QImage imgAvatar = QImage(QString::fromStdU16String(sImagePath));
if(imgAvatar.isNull())
imgAvatar = QImage(GetDefaultAvatar(uSex));
if((uStatus==0||uStatus==5)&&tDelegateStyle.bGrayAvatarImg)
imgAvatar = imgAvatar.convertToFormat(QImage::Format_Grayscale8,Qt::AutoColor);
painter->drawImage(avatarRect, imgAvatar);
};break;
}
painter->restore();
if((tDelegateStyle.bIconViewModel||!tDelegateStyle.bShowHeadLines)&&tDelegateStyle.uShowStatusIco){
painter->save();
painter->setPen(Qt::NoPen);
if(uStatus!=0&&uStatus!=5){
painter->setBrush(tDelegateStyle.cUsersNodesClr);
painter->drawEllipse(stateFrm);
}
if(uStatus>0&&uStatus<8&&uStatus!=5){
///0脱机,1联机,2勿扰,3离开,4忙碌,5隐身,6自定义
switch (uStatus) {
case 0:painter->drawImage(stateRect, QImage(sOffLineImages));break;
case 1:painter->drawImage(stateRect, QImage(sOnlineImgLogo));break;
case 2:painter->drawImage(stateRect, QImage(sNotDisturbImg));break;
case 3:painter->drawImage(stateRect, QImage(sLeaveLogosImg));break;
case 4:painter->drawImage(stateRect, QImage(sBusyStatusImg));break;
case 5:painter->drawImage(stateRect, QImage(sOffLineImages));break;
case 6:painter->drawImage(stateRect, QImage(sCallMeStatImg));break;
case 7:painter->drawImage(stateRect, QImage(sCustomizeImgs));break;
}
}
painter->restore();
}
}
事件和其他代码示例
/// 重写视图项尺寸定义
QSize Lncf_QFriendsItemd::sizeHint(const QStyleOptionViewItem &option, const QModelIndex &index) const
{
//如果是分组
Q_UNUSED(index)
auto size = QStyledItemDelegate::sizeHint(option, index);
if(index.data(LQFRIENDSLIST_ROLES::FRIENDSLIST_HEADE).toBool()){
Lncf_QFriendsListv *pListView = static_cast(index.model()->parent());
if(pListView) {
return QSize(pListView->viewport()->width(), tDelegateStyle.bIconViewModel?this->tDelegateStyle.uGroupsHeights*1.5:this->tDelegateStyle.uGroupsHeights);
}
}
else{ //如果是用户
if(!tDelegateStyle.bIconViewModel){
uint uRowHeight = this->tDelegateStyle.uItemHeightVal;
if(this->tDelegateStyle.uItemsStyleIds==1)
uRowHeight = index.data(LQFRIENDSLIST_ROLES::FRIENDSLIST_ACTIV).toBool()?this->tDelegateStyle.uItemHeightVal:this->tDelegateStyle.uItemMinHeight;
return QSize(option.rect.width(), uRowHeight);
}
else
{
//size.rheight() = uIconGridHeigh;
return QSize(this->tDelegateStyle.uIconGridWidth, this->tDelegateStyle.uIconGridHeigh);
}
}
return size;
}
/// 重写视图项编辑事件
bool Lncf_QFriendsItemd::editorEvent(QEvent *event,QAbstractItemModel *model,const QStyleOptionViewItem &option,const QModelIndex &index)
{
if(index.isValid())
{
auto mouseEvent = static_cast(event);
if(!mouseEvent) {
return QStyledItemDelegate::editorEvent(event, model, option, index);
}
//判断是分组还是用户
if(index.data(LQFRIENDSLIST_ROLES::FRIENDSLIST_HEADE).toBool())
{
QVariant varGroup = index.data(LQFRIENDSLIST_ROLES::FRIENDSLIST_GROUP);
LNCFQFRIENDS_LISTGROUP ItemGroupData = varGroup.value();
//printf("group id:%zd,expand:%d\n",ItemGroupData.uItemsGroupIds,bExpand);
switch (mouseEvent->type()) {
case QEvent::MouseMove:
{
}break;
case QEvent::MouseButtonRelease:
{
if(mouseEvent->button() != Qt::LeftButton){
//分组右键单击事件
emit GroupsRightClicked(ItemGroupData,mouseEvent->globalPos(),index);
return QStyledItemDelegate::editorEvent(event, model, option, index);
}
}break;
case QEvent::MouseButtonDblClick:
{
if(mouseEvent->button() != Qt::LeftButton){
return QStyledItemDelegate::editorEvent(event, model, option, index);
}
if(this->tDelegateStyle.uExpandGroupId>1&&this->tDelegateStyle.uExpandGroupId<4){
bool bExpand = index.data(LQFRIENDSLIST_ROLES::FRIENDSLIST_EXPAN).toBool();
if(this->tDelegateStyle.uExpandGroupId==2){
if(GetElementRectan(option, index,true, 0).contains(mouseEvent->pos()))
{
model->setData(index, bExpand?false:true, LQFRIENDSLIST_ROLES::FRIENDSLIST_EXPAN);
emit GroupItemsExpand(bExpand?false:true,!bNavGroupStyle?ItemGroupData.uItemsGroupIds:index.data(LQFRIENDSLIST_ROLES::FRIENDSLIST_INDEX).toULongLong(),index);
break;
}
}
if(this->tDelegateStyle.uExpandGroupId==3)
{
model->setData(index, bExpand?false:true, LQFRIENDSLIST_ROLES::FRIENDSLIST_EXPAN);
emit GroupItemsExpand(bExpand?false:true,!bNavGroupStyle?ItemGroupData.uItemsGroupIds:index.data(LQFRIENDSLIST_ROLES::FRIENDSLIST_INDEX).toULongLong(),index);
break;
}
}
}break;
}
}
else{
if(this->bHasMouseEvent){
{
model->setData(index, false, LQFRIENDSLIST_ROLES::FRIENDAVATAR_HOVER); // 鼠标移入头像
model->setData(index, false, LQFRIENDSLIST_ROLES::FRIENDVIPICO_HOVER); // 鼠标移入VIP按钮
model->setData(index, false, LQFRIENDSLIST_ROLES::FRIENDDEVICE_HOVER); // 鼠标移入设备按钮
model->setData(index, false, LQFRIENDSLIST_ROLES::FRIENDRSUMARY_HOVER); // 鼠标移入摘要数据区域
model->setData(index, false, LQFRIENDSLIST_ROLES::FRIENDVIDEOS_HOVER); // 鼠标移入视频通话按钮区域
model->setData(index, false, LQFRIENDSLIST_ROLES::FRIENDAUDIOS_HOVER); // 鼠标移入音频通话按钮区域
model->setData(index, false, LQFRIENDSLIST_ROLES::FRIENDSMORES_HOVER); // 鼠标移入更多选项按钮区域
}
QVariant varUsers = index.data(LQFRIENDSLIST_ROLES::FRIENDSLIST_ITEMS);
LNCFQFRIENDS_LISTVIEWS ItemUsersData = varUsers.value();
//0:头像区域,1设备图标,2vip图标,3视频通话区域,4音频通话区域,5更多选项区域,6名称区域,7摘要区域”,分组项索引0节点图标,1名称,2统计数量
switch (mouseEvent->type()) {
case QEvent::MouseMove:
{
if(bShowItemsTips)QToolTip::hideText();
//鼠标移入设备图标区域
auto device_hover = GetElementRectan(option, index,false, 1).contains(mouseEvent->pos());
model->setData(index, (ItemUsersData.uDeviceTypeIds>=0&&ItemUsersData.uDeviceTypeIds<4)?device_hover:false, LQFRIENDSLIST_ROLES::FRIENDDEVICE_HOVER);
}break;
case QEvent::MouseButtonRelease:
{
//发送头像区域单击信号
if(GetElementRectan(option, index,false, 0).contains(mouseEvent->pos()))
{
emit ItemElementClick(0,index);
return QStyledItemDelegate::editorEvent(event, model, option, index);
}
model->setData(index,true,LQFRIENDSLIST_ROLES::FRIENDSLIST_CLICK);
}break;
}
}
}
}
return QStyledItemDelegate::editorEvent(event, model, option, index);
}
受限于篇幅限制,无法提供所有代码。上面只是委托绘制代码,下面为列表部分头文件代码
class LNCF_QTSOCIALLIBS_API Lncf_QFriendsListv : public Lncf_IndexNavListv
{
Q_OBJECT
Q_PROPERTY(QString sDevicePcIcons READ GetDevicePcIcons WRITE SetDevicePcIcons) //PC客户端图标
Q_PROPERTY(QString sDeviceMbIcons READ GetDeviceMbIcons WRITE SetDeviceMbIcons) //手机客户端图标
Q_PROPERTY(QString sDeviceWbIcons READ GetDeviceWbIcons WRITE SetDeviceWbIcons) //web客户端图标
Q_PROPERTY(QString sOtherAppIcons READ GetOtherAppIcons WRITE SetOtherAppIcons) //其它应用图标
Q_PROPERTY(QString sVipLogoImages READ GetVipLogoImages WRITE SetVipLogoImages) //会员图标
Q_PROPERTY(QString sSvipLogoImage READ GetSvipLogoImage WRITE SetSvipLogoImage) //超级会员图标
Q_PROPERTY(QString sOnlineImgLogo READ GetOnlineImgLogo WRITE SetOnlineImgLogo) //在线状态图标
Q_PROPERTY(QString sOffLineImages READ GetOffLineImages WRITE SetOffLineImages) //离线状态图标
Q_PROPERTY(QString sLeaveLogosImg READ GetLeaveLogosImg WRITE SetLeaveLogosImg) //离开状态图标
Q_PROPERTY(QString sBusyStatusImg READ GetBusyStatusImg WRITE SetBusyStatusImg) //忙碌状态图标
Q_PROPERTY(QString sStealthImages READ GetStealthImages WRITE SetStealthImages) //隐身状态图标
Q_PROPERTY(QString sNotDisturbImg READ GetNotDisturbImg WRITE SetNotDisturbImg) //勿扰状态图标
Q_PROPERTY(QString sCustomizeImgs READ GetCustomizeImgs WRITE SetCustomizeImgs) //自定义状态图标
Q_PROPERTY(QString sCallMeStatImg READ GetCallMeStatImg WRITE SetCallMeStatImg) //与我联系状态图标
Q_PROPERTY(QString sSourceAppsImg READ GetSourceAppsImg WRITE SetSourceAppsImg) //默认来源应用图标
Q_PROPERTY(QString sGroupExpanImg READ GetGroupExpanImg WRITE SetGroupExpanImg) //分组展开图标
Q_PROPERTY(QString sGroupCloseImg READ GetGroupCloseImg WRITE SetGroupCloseImg) //分组关闭图标
Q_PROPERTY(QString sVideoChatImgs READ GetVideoChatImgs WRITE SetVideoChatImgs) //视频会话图标
Q_PROPERTY(QString sAudioChatImgs READ GetAudioChatImgs WRITE SetAudioChatImgs) //音频通话图标
Q_PROPERTY(QString sMoreOptionImg READ GetMoreOptionImg WRITE SetMoreOptionImg) //更多选项图标
Q_PROPERTY(bool bHasMouseEvent READ GetItemsMouseEvt WRITE SetItemsMouseEvt) //是否启用鼠标事件
Q_PROPERTY(bool bHasHoverEvent READ GetHasHoverEvent WRITE SetHasHoverEvent) //是否处理鼠标悬停事件
Q_PROPERTY(bool bShowItemsTips READ GetShowItemsTips WRITE SetShowItemsTips) //是否显示鼠标悬停时提示信息
Q_PROPERTY(uint uGroupStyleIds READ GetGroupStyleIds WRITE SetGroupStyleIds) //分组风格
Q_PROPERTY(uint uItemsStyleIds READ GetItemsStyleIds WRITE SetItemsStyleIds) //列表项风格
Q_PROPERTY(uint uExpandGroupId READ GetExpandGroupId WRITE SetExpandGroupId) //展开和闭合分组操作方式
Q_PROPERTY(uint uUserRowHeight READ GetUserRowHeight WRITE SetUserRowHeight); //好友行高
Q_PROPERTY(uint uGroupsHeights READ GetGroupsHeights WRITE SetGroupsHeights); //分组项行高
Q_PROPERTY(bool bNavGroupStyle READ GetNavGroupStyle WRITE SetNavGroupStyle); //分组类别
Q_PROPERTY(bool bHasOnlyOnline READ GetHasOnlyOnline WRITE SetHasOnlyOnline); //显示联机用户或全部
Q_PROPERTY(bool bShowGroupItem READ GetShowGroupItem WRITE SetShowGroupItem); //是否显示好友分组
public:
explicit Lncf_QFriendsListv(QWidget *parent = nullptr);
~ Lncf_QFriendsListv();
protected:
/// 初始化列表虚函数
/// \brief InitFriendsList
///
virtual void InitFriendsList();
private:
/// 初始导航组
/// \brief InitNavigatGroup
///
void InitNavigatGroup();
private:
QStandardItemModel *pListItemModel = nullptr; //列表模型对象指针
Lncf_QFriendsItemd *pItemsDelegate = nullptr; //列表项委托对象指针
Lncf_QFriendSortlm *pSortFilteMode = nullptr; //列表筛选器模型对象指针
/// 是否启用鼠标跟踪
/// \brief bMouseTracking
///
bool bMouseTracking = false;
/// 是否为索引分组类别,如果为索引分组则不显示用户分组,而是以A-Z的字母顺序分组
/// \brief bNavGroupStyle
///
bool bNavGroupStyle = false;
/// 仅显示在线用户
/// \brief bHasOnlyOnline
///
bool bHasOnlyOnline = false;
/// 是否显示分组
/// \brief bShowGroupItem
///
bool bShowGroupItem = true;
/// 导航分组是否初始化
/// \brief bInitNaviGroup
///
bool bInitNaviGroup = false;
/// 索引分组文本
/// \brief sNaviTextsList
///
QList sNaviTextsList;
public:
/// 获取指定索引模型分组数据
/// \brief GetItemsViewData
/// \param index
/// \return
///
LNCFQFRIENDS_LISTGROUP GetItemGroupData(const QModelIndex &index) const;
/// 获取指定索引模型视图数据
/// \brief GetItemsViewData
/// \param index
/// \return
///
LNCFQFRIENDS_LISTVIEWS GetItemsUserData(const QModelIndex &index) const;
/// 获取指定索引模型自定义应用数据
/// \brief GetItemsAppsData
/// \param index
/// \param bGroup :是否为分组项
/// \return
///
QVariant GetItemsAppsData(const QModelIndex &index,bool &bGroup) const;
/// 获取会话列表所有数据链表
/// \brief GetItemsDataList
/// \param vList
/// \return
///
bool GetItemsDataList(std::vector &vList);
public:
实现QListView函数//
void setViewMode(ViewMode mode);
void setSpacing(int space);
void setGridSize(const QSize &size);
public:
/// 添加好友列表分组
/// \brief AddFriendsGroup
/// \param data
/// \param appData
///
void AddFriendsGroup(LNCFQFRIENDS_LISTGROUP data);
/// 添加会话项目
/// \brief AddSessionItems
/// \param data :默认视图数据
/// \param appData :自定义app数据,此数据由app自行维护,默认情况下此数据不赋值
///
void AddFriendsUsers(LNCFQFRIENDS_LISTVIEWS data);
/// 添加一个好友分组列表项
/// \brief AddFriendsLists
/// \param data
///
void AddFriendsLists(LNCFQFRIENDS_LISTDATAS data);
/// 设置会话列表数据集合
/// \brief SetSessionDatas
/// \param vList : 包含两条数据属性,1视图数据(LNCFQSESSION_VIEWDATAS),2应用自定义数据(QVariant)
///
void SetFriendsDatas(std::vector &vList);
/// 删除分组,如果该分组有成员则不会被删除
/// \brief RemoveGroupData
/// \param uGroupID :分组ID
/// \param uErrMsg :返回错误引用参数
/// \return
///
bool RemoveGroupData(uint64_t uGroupID,std::u16string &uErrMsg);
/// 删除好友成员
/// \brief RemoveUsersData
/// \param tUserInf:为指定的好友数据结构,只用到了此结构体的三个变量,gViewsItemsIds;uViewsItemsIds;sViewsItemsIds;
/// \param uErrMsg :返回错误引用参数
/// \return
///
bool RemoveUsersData(LNCFQFRIENDS_LISTVIEWS tUserInf,std::u16string &uErrMsg);
/// 移动用户分组
/// \brief MoveUsersGroups
/// \param tUserInf
/// \param uOldGroupId
/// \param uNewGroupId
/// \return
///
bool MoveUsersGroups(LNCFQFRIENDS_LISTVIEWS tUserInf,uint64_t uOldGroupId,uint64_t uNewGroupId,std::u16string &uErrMsg);
/// 好友数据是否存在
/// \brief HasFriendsExist
/// \param data
/// \return
///
bool HasFriendsExist(LNCFQFRIENDS_LISTDATAS data);
/// 检查目标属性分组是否存在。主要检查分组ID
/// \brief HasGroupsExists
/// \param data
/// \return
///
bool HasGroupsExists(LNCFQFRIENDS_LISTGROUP data);
/// 检查目标联系人属性是否存在,检查三个itemid属性
/// \brief HasContactExist
/// \param data,主要用结构体三个参数:gViewsItemsIds,uViewsItemsIds,sViewsItemsIds
/// \return
///
bool HasContactExist(LNCFQFRIENDS_LISTVIEWS data);
/// 更新用户数据
/// \brief UpdateUserDatas
/// \param data :默认视图数据
///
bool UpdateUsersData(LNCFQFRIENDS_LISTVIEWS data,std::u16string &uErrMsg);
/// 更新分组数据
/// \brief UpdateGroupData
/// \param data :默认视图数据
///
bool UpdateGroupData(LNCFQFRIENDS_LISTGROUP data,std::u16string &uErrMsg);
/// 根据模型索引更新模型用户数据
/// \brief UpdateUsersData
/// \param index :索引ID
/// \param data :视图数据
///
bool UpdateUsersData(const QModelIndex &index,LNCFQFRIENDS_LISTVIEWS data,std::u16string &uErrMsg);
/// 根据模型索引更新模型分组数据
/// \brief UpdateUsersData
/// \param index :索引ID
/// \param data :视图数据
///
bool UpdateGroupData(const QModelIndex &index,LNCFQFRIENDS_LISTGROUP data,std::u16string &uErrMsg);
/// 设置目标索引的自定义app数据
/// \brief SetItemAppsData
/// \param index :模型索引ID
/// \param appData :应用数据,数据设置不会查重,而是直接替换原有数据[如果存在]。因此如果有必要请先获取然后在写入
///
bool SetItemAppsData(const QModelIndex &index,QVariant appData,std::u16string &uErrMsg);
/// 激活指定的用户项
/// \brief ActiveUsersItem
/// \param tagUser
///
bool ActiveUsersItem(const LNCFQFRIENDS_LISTVIEWS tagUser,std::u16string &uErrMsg);
/// 更新列表风格
/// \brief UpdateListDatas
///
void UpdateListDatas();
/// 展开所有节点
/// \brief ExpandAllsNodes
///
void ExpandAllsNodes();
/// 闭合所有节点
/// \brief ClosureAllNodes
///
void ClosureAllNodes();
/// 获取联系人总数
/// \brief GetFriendsCount
/// \return
///
uint64_t GetFriendsCount();
public:
/*属性代码段省略*/
private slots:
/// 处理分组展开或收起
/// \brief HandGroupsExpand
/// \param bExpand 是否展开,true表示展开否则为收起
/// \param uGroup :分组ID
/// \param index :模型索引ID
///
void HandGroupsExpand(const bool &bExpand,const uint64_t uGroup, const QModelIndex &index);
/// 处理列表项单击
/// \brief HandItemsClicked
/// \param index
///
void HandItemsClicked(const QModelIndex &index);
protected:
void resizeEvent(QResizeEvent *event) override;
private slots:
/// 列表视图元素单击事件槽
/// \brief ItemsElementEvts
/// \param nElement 元素索引定义[0:头像,1设备图标,2vip图标,3未读消息,4屏蔽按钮]
/// \param index
///
void OnUserElementClick(const int &nElement,const QModelIndex &index);
/// 列表视图元素双击事件槽
/// \brief ItemElementDbClick
/// \param nElement
/// \param index
///
void OnUserElementDbClick(const int &nElement,const QModelIndex &index);
/// 列表视图Ui元素鼠标悬停事件
/// \brief OnElementsHovers
/// \param nElement 元素索引定义 0:头像,1设备图标,2vip图标,3视频通话,4音频通话,5其它选项
/// \param index
/// \param itemRect 当前项目矩形区域
/// \param evtPos 鼠标悬停位置坐标[当前窗口]
/// \param evtGlobalPos 鼠标悬停位置坐标[桌面窗口]
///
void OnUserElementsHovers(const int &nElement,const QModelIndex &index,const QRect &itemRect,const QPoint &evtPos,const QPoint &evtGlobalPos);
/// 分组右键单击事件
/// \brief OnGroupRightClick
/// \param tGroupInf
/// \param point
///
void OnGroupRightClick(const LNCFQFRIENDS_LISTGROUP &tGroupInf,const QPoint &point,const QModelIndex &index);
/// 好友右键单击事件
/// \brief OnUsersRightClick
/// \param tUsersInf
/// \param point
///
void OnUsersRightClick(const LNCFQFRIENDS_LISTVIEWS &tUsersInf,const QPoint &point,const QModelIndex &index);
/// 处理导航按钮选中事件
/// \brief NaviBtnCheckEvts
/// \param uIndex :选中的按钮索引0表示所有#按钮,1-26表示字母A到Z,27=数字按钮,28=其它按钮
///
void OnNaviBtnCheckEvt(const uint uIndex);
};
列表部分实现代码
Lncf_QFriendsListv::Lncf_QFriendsListv(QWidget *parent)
:Lncf_IndexNavListv(parent)
{
this->InitNavBaseList(false,false);
this->InitFriendsList();
}
Lncf_QFriendsListv::~Lncf_QFriendsListv()
{
}
/// 初始化列表虚函数
/// \brief InitFriendsList
///
void Lncf_QFriendsListv::InitFriendsList()
{
sNaviTextsList = {u"A",u"B",u"C",u"D",u"E",u"F",u"G",u"H",u"I",u"J",u"K",u"L",u"M",u"N",u"O",u"P",u"Q",u"R",u"S",u"T",u"U",u"V",u"W",u"X",u"Y",u"Z",u"❉",u"☼"};
SetNavPaddingVal(9);
pItemsDelegate = new Lncf_QFriendsItemd(this);
this->setItemDelegate(pItemsDelegate);
pListItemModel = new QStandardItemModel(this);
pSortFilteMode = new Lncf_QFriendSortlm(this);
pSortFilteMode->setSourceModel(pListItemModel); //将model放入代理中
this->setModel(pSortFilteMode); //在视图中安装代理
connect(pItemsDelegate, &Lncf_QFriendsItemd::GroupItemsExpand, this, &Lncf_QFriendsListv::HandGroupsExpand);
connect(pItemsDelegate, &Lncf_QFriendsItemd::ItemElementClick, this, &Lncf_QFriendsListv::OnUserElementClick);
connect(pItemsDelegate, &Lncf_QFriendsItemd::ItemElementDbClick, this, &Lncf_QFriendsListv::OnUserElementDbClick);
connect(pItemsDelegate, &Lncf_QFriendsItemd::ItemsElementHovers, this, &Lncf_QFriendsListv::OnUserElementsHovers);
connect(pItemsDelegate, &Lncf_QFriendsItemd::GroupsRightClicked, this, &Lncf_QFriendsListv::OnGroupRightClick);
connect(pItemsDelegate, &Lncf_QFriendsItemd::UserRightClickEvts,this, &Lncf_QFriendsListv::OnUsersRightClick);
connect(this, &Lncf_IndexNavListv::clicked, this, &Lncf_QFriendsListv::HandItemsClicked);
connect(this, &Lncf_IndexNavListv::NavBtnCheckEvts, this, &Lncf_QFriendsListv::OnNaviBtnCheckEvt);
this->setEditTriggers(QAbstractItemView::NoEditTriggers);
this->setResizeMode(Lncf_IndexNavListv::Adjust );
this->SetItemsMouseEvt(bMouseTracking);
this->setVerticalScrollMode(ScrollPerPixel);
}
/// 初始导航组
/// \brief InitNavigatGroup
///
void Lncf_QFriendsListv::InitNavigatGroup()
{
// A~Z
for(int i = 0; i < 26; i++)
{
QLatin1Char ch(i + 65);
auto itemGroup = new QStandardItem(QString(ch));
itemGroup->setData(true, LQFRIENDSLIST_ROLES::FRIENDSLIST_HEADE);
LNCFQFRIENDS_LISTGROUP groupData;
groupData.uItemsNavIndex = i+1;
groupData.uItemsGroupIds = i+65;
groupData.sItemGroupName = sNaviTextsList[i];
itemGroup->setData(i+1,LQFRIENDSLIST_ROLES::FRIENDSLIST_INDEX);
itemGroup->setData(QVariant::fromValue(groupData), LQFRIENDSLIST_ROLES::FRIENDSLIST_GROUP);
itemGroup->setData(true, LQFRIENDSLIST_ROLES::FRIENDSLIST_EXPAN);
itemGroup->setData(1, LQFRIENDSLIST_ROLES::FRIENDGROUP_TYPES);
itemGroup->setData(0, LQFRIENDSLIST_ROLES::FRIENDGROUP_DATAS);
itemGroup->setData((i+1)*100, LQFRIENDSLIST_ROLES::FRIENDINDEX_DATAS);
pListItemModel->appendRow(itemGroup);
}
//数字开头,标识为❉, 索引未26
auto itemNumber = new QStandardItem(QString::fromStdU16String(sNaviTextsList[26]));
itemNumber->setData(true, LQFRIENDSLIST_ROLES::FRIENDSLIST_HEADE);
LNCFQFRIENDS_LISTGROUP numberData;
numberData.uItemsNavIndex = 27;
numberData.uItemsGroupIds = 26+65;
numberData.sItemGroupName = sNaviTextsList[26];
itemNumber->setData(QVariant::fromValue(numberData), LQFRIENDSLIST_ROLES::FRIENDSLIST_GROUP);
itemNumber->setData(true, LQFRIENDSLIST_ROLES::FRIENDSLIST_EXPAN);
itemNumber->setData(1, LQFRIENDSLIST_ROLES::FRIENDGROUP_TYPES);
itemNumber->setData(27,LQFRIENDSLIST_ROLES::FRIENDSLIST_INDEX);
itemNumber->setData(0, LQFRIENDSLIST_ROLES::FRIENDGROUP_DATAS);
itemNumber->setData(27*100, LQFRIENDSLIST_ROLES::FRIENDINDEX_DATAS);
pListItemModel->appendRow(itemNumber);
//其它字符以☼表示,索引为27
auto itemOther = new QStandardItem(QString::fromStdU16String(sNaviTextsList[27]));
itemOther->setData(true, LQFRIENDSLIST_ROLES::FRIENDSLIST_HEADE);
LNCFQFRIENDS_LISTGROUP otherData;
otherData.uItemsNavIndex = 28;
otherData.uItemsGroupIds = 27+65;
otherData.sItemGroupName = sNaviTextsList[27];
itemOther->setData(QVariant::fromValue(otherData), LQFRIENDSLIST_ROLES::FRIENDSLIST_GROUP);
itemOther->setData(true, LQFRIENDSLIST_ROLES::FRIENDSLIST_EXPAN);
itemOther->setData(1, LQFRIENDSLIST_ROLES::FRIENDGROUP_TYPES);
itemOther->setData(28,LQFRIENDSLIST_ROLES::FRIENDSLIST_INDEX);
itemOther->setData(0, LQFRIENDSLIST_ROLES::FRIENDGROUP_DATAS);
itemOther->setData(28*100, LQFRIENDSLIST_ROLES::FRIENDINDEX_DATAS);
pListItemModel->appendRow(itemOther);
bInitNaviGroup = true;
}
/// 获取指定索引模型分组数据
/// \brief GetItemsViewData
/// \param index
/// \return
///
LNCFQFRIENDS_LISTGROUP Lncf_QFriendsListv::GetItemGroupData(const QModelIndex &index) const
{
if(index.isValid()&&pSortFilteMode->data(index,LQFRIENDSLIST_ROLES::FRIENDSLIST_HEADE).toBool())
{
return pSortFilteMode->data(index,LQFRIENDSLIST_ROLES::FRIENDSLIST_GROUP).value();
}
return LNCFQFRIENDS_LISTGROUP();
}
/// 获取指定索引模型视图数据
/// \brief GetItemsViewData
/// \param index
/// \return
///
LNCFQFRIENDS_LISTVIEWS Lncf_QFriendsListv::GetItemsUserData(const QModelIndex &index) const
{
if(index.isValid()&&!pSortFilteMode->data(index,LQFRIENDSLIST_ROLES::FRIENDSLIST_HEADE).toBool())
{
return pSortFilteMode->data(index,LQFRIENDSLIST_ROLES::FRIENDSLIST_ITEMS).value();
}
return LNCFQFRIENDS_LISTVIEWS();
}
/// 获取指定索引模型自定义应用数据
/// \brief GetItemsAppsData
/// \param index
/// \return
///
QVariant Lncf_QFriendsListv::GetItemsAppsData(const QModelIndex &index,bool &bGroup) const
{
if(index.isValid())
{
bGroup = pSortFilteMode->data(index,LQFRIENDSLIST_ROLES::FRIENDSLIST_HEADE).toBool();
if(bGroup)
return pSortFilteMode->data(index,LQFRIENDSLIST_ROLES::FRIENDSLIST_GROUP).value().tGroupAppsData;
else
return pSortFilteMode->data(index,LQFRIENDSLIST_ROLES::FRIENDSLIST_ITEMS).value().tItemsAppsData;
}
return QVariant();
}
/// 获取会话列表所有数据链表
/// \brief GetItemsDataList
/// \param vList
/// \return
///
bool Lncf_QFriendsListv::GetItemsDataList(std::vector &vList)
{
int rowCount = pSortFilteMode->rowCount();
for(int i=0;iindex(i, 0);
if(Index.isValid()){
LNCFQFRIENDS_LISTDATAS tagItemData;
if(pSortFilteMode->data(Index,LQFRIENDSLIST_ROLES::FRIENDSLIST_HEADE).toBool()&&pSortFilteMode->data(Index,LQFRIENDSLIST_ROLES::FRIENDGROUP_TYPES).toUInt()==0){
tagItemData.tFriendsGroups = pSortFilteMode->data(Index,LQFRIENDSLIST_ROLES::FRIENDSLIST_GROUP).value();
LNCFQFRIENDS_LISTVIEWS itemView;
for(int x=0;xindex(x, 0);
if(!pSortFilteMode->data(sIndex,LQFRIENDSLIST_ROLES::FRIENDSLIST_HEADE).toBool()){
itemView = pSortFilteMode->data(sIndex,LQFRIENDSLIST_ROLES::FRIENDSLIST_ITEMS).value();
tagItemData.vSubsListItems.push_back(itemView);
}
}
}
vList.push_back(tagItemData);
}
}
return vList.size()>0;
}
实现QListView函数//
void Lncf_QFriendsListv::setViewMode(ViewMode mode)
{
if(mode==Lncf_IndexNavListv::IconMode){
pItemsDelegate->SetHasIconsModel(true);
this->setContentsMargins(3,3,3,3);
this->update();
}
else{
pItemsDelegate->SetHasIconsModel(false);
this->setContentsMargins(1,1,1,1);
}
Lncf_IndexNavListv::setViewMode(mode);
}
void Lncf_QFriendsListv::setSpacing(int space)
{
Lncf_IndexNavListv::setSpacing(space);
}
void Lncf_QFriendsListv::setGridSize(const QSize &size)
{
pItemsDelegate->SetIconsGridSize(size.width(),size.height());
//QListView::setGridSize(size);
}
/// 添加好友列表分组
/// \brief AddFriendsGroup
/// \param data
/// \param appData
///
void Lncf_QFriendsListv::AddFriendsGroup(LNCFQFRIENDS_LISTGROUP data)
{
if(HasGroupsExists(data))
return;
QStandardItem *pItem = new QStandardItem;
pItem->setData(true, LQFRIENDSLIST_ROLES::FRIENDSLIST_HEADE);
pItem->setData(true, LQFRIENDSLIST_ROLES::FRIENDSLIST_EXPAN);
pItem->setData(0, LQFRIENDSLIST_ROLES::FRIENDGROUP_TYPES);
pItem->setData(data.uItemsGroupIds+1*10, LQFRIENDSLIST_ROLES::FRIENDGROUP_DATAS);
pItem->setData(0, LQFRIENDSLIST_ROLES::FRIENDINDEX_DATAS);
pItem->setData(QVariant::fromValue(data), LQFRIENDSLIST_ROLES::FRIENDSLIST_GROUP);
pListItemModel->appendRow(pItem);
UpdateListDatas();
}
/// 添加会话项目
/// \brief AddSessionItems
/// \param data :默认视图数据
/// \param appData :自定义app数据,此数据由app自行维护,默认情况下此数据不赋值
///
void Lncf_QFriendsListv::AddFriendsUsers(LNCFQFRIENDS_LISTVIEWS data)
{
if(HasContactExist(data))
return;
QString sName = QString::fromStdU16String(data.uItemsShowName);
QStandardItem *pUserItem = new QStandardItem(sName);
pUserItem->setData(false, LQFRIENDSLIST_ROLES::FRIENDSLIST_HEADE);
pUserItem->setData(false, LQFRIENDSLIST_ROLES::FRIENDSLIST_ACTIV);
pUserItem->setData(false, LQFRIENDSLIST_ROLES::FRIENDSLIST_CLICK);
pUserItem->setData(data.uItemsGroupIds+1*10+1, LQFRIENDSLIST_ROLES::FRIENDGROUP_DATAS);
pUserItem->setData(Lncf_NavCharHelper::GetNavigateIndexVal(sName.at(0)),LQFRIENDSLIST_ROLES::FRIENDSLIST_INDEX);
pUserItem->setData(Lncf_NavCharHelper::GetNavigateIndexVal(sName.at(0))*100+1, LQFRIENDSLIST_ROLES::FRIENDINDEX_DATAS);
pUserItem->setData(data.uOnlinesStatus!=5&&data.uOnlinesStatus!=0,LQFRIENDSLIST_ROLES::FRIENDONLINE_STATE);
pUserItem->setData(QVariant::fromValue(data), LQFRIENDSLIST_ROLES::FRIENDSLIST_ITEMS);
pListItemModel->appendRow(pUserItem);
UpdateListDatas();
}
结束:由于整体代码很长,而篇幅有限,因此给个大概的思路和参考。在ListView中继承的Lncf_IndexNavListv我们将在后面的文章中给出,此类主要实现了列表导航封装。