Qt实现模拟微信联系人列表

联系人列表主要由两部分组成,每个联系人的状态消息框和一个列表组成,因此,我们用QListWidget和自定义的item来实现这个列表。

1、自定义message item

如图:
在这里插入图片描述
一个item由头像、暱称、最近的一条消息、最近一条消息的时间和关闭按钮组成,因此我们首先用designer按照需要的东西选择合适的控件进行绘制界面。
界面绘制完成之后,就需要完成界面所需要的一些槽函数,在这边,我们应该有一个主题思想是需要确定的,就是每个item所需要的数据由自己管理,这样,在管理这些数据的时候就会比较方便, 会避免在外界管理所有item的数据而形成的比较庞大的数据结构。
实现槽函数的方式有很多,这儿我们采用自定义属性的方式来确定一些需要的数据。题外:Qt的每个原生控件都有一些被 Q_PROPERTY qproperty- 定义的原始属性,Q_PROPERTY是一个宏。

# define Q_PROPERTY(...) QT_ANNOTATE_CLASS(qt_property, __VA_ARGS__)

我们也使用这种方法来添加一些自定义item的属性。如下:

class MsgItem : public QWidget
{    Q_OBJECT
     Q_PROPERTY(QString msg READ msg WRITE setMsg DESIGNABLE true)
 public:
    explicit MsgItem(QWidget *parent = 0);
     ~MsgItem();  
 private:
 	Ui::MsgItem *ui;
 };

使用这种自定义属性的方式需要实现的公有方法的格式是比较固定的。如下:

QString msg() const;
void setMsg(const QString& msg);

按照上面的方式实现其余的方法。
实现的列表点击每个item的时候会有选中的效果,这种选中的效果有两种实现方式,一种是最正常的方式,也就是借用QListWidget的itemClicked(QListWidgetItem*)信号来按部就班的实现;另一种是通过集成QWidget的mouse事件来模拟选中的效果。这个例子中两种方式都有涉猎。最终的item类定义如下:

class MsgItem : public QWidget
{    Q_OBJECT
     //可按照下面的方法自己添加需要的功能
     Q_PROPERTY(QString msg READ msg WRITE setMsg DESIGNABLE true)
     Q_PROPERTY(QString header READ header WRITE setHeader DESIGNABLE true)
     Q_PROPERTY(QString name READ name WRITE setName DESIGNABLE true)
     Q_PROPERTY(QString uuid READ uuid WRITE setUuid DESIGNABLE true)
     Q_PROPERTY(QString datetime READ datetime WRITE setDatetime DESIGNABLE true)
     Q_PROPERTY(int count READ count WRITE setCount DESIGNABLE true)

public:
    explicit MsgItem(QWidget *parent = 0);
     ~MsgItem();

    QString msg() const;
    void setMsg(const QString& msg);

    QString header() const;
    void setHeader(const QString& header);

    QString uuid() const
    {
        return m_uuid;
    }
    void setUuid(const QString& uuid)
    {
        m_uuid = uuid;
    }

    QString name() const;
    void setName(const QString& name);

    QString datetime() const;
    void setDatetime(const QString& datetime);

    int count() const;
    void setCount(const int count);

    void setData( const QString &msg,
                        const QString& nick,
                        const QString& header,
                        const QString& name,
                        const QString& time );

    void selected(bool selected);

protected:
     virtual void mousePressEvent( QMouseEvent *event );


private slots:
     void on_btnDeleteClicked();

signals:
     void signal_selected(MsgItem*);
     void signal_delete(QWidget*);

private:
     Ui::MsgItem *ui;
     QString m_header{""};
     QString m_uuid{""};
};

其中的实现也比较简单,就是简单的设置一些界面的信息显示。我们看一下选中的效果实现:

void MsgItem::selected(bool selected)
{
    ui->widgetMain->setStyleSheet(selected ? 
    QString("QWidget#widgetMain{ background-color: #F2F2F2; border-bottom: 1px solid #F2F2F2;}")
  : QString("QWidget#widgetMain{ background-color: #FFFFFF; border-bottom: 1px solid #F2F2F2;}" ));
    ui->btnClose->setVisible(selected);
    setCount(0);
}

选中的效果是通过设置不同的qss来实现。

2、容器的实现

容器的实现基本上没什么比较注重的地方,使用QListWidget的原始功能就能够实现。为了方便设置选中的效果的切换,我们需要定义一个成员变量来记录当前的item,当进行切换时,先设置上一个为未选中,再设置下一个为选中即可。

因为item是自定义的,因此创建item的时候需要将自定义的item通过setItemWidget(QListWidgetItem*, QWidget*)方法加载到原始的item上。如下所示:

void ListTemplate::AddWidgetMsgItem()
{
    auto pMsgItem = new MsgItem();
    QListWidgetItem *item = new QListWidgetItem;
    item->setSizeHint( QSize(360, 70));
    pMsgItem->setCount(99);
    ui->listWidget->addItem(item);
    ui->listWidget->setItemWidget(item, pMsgItem);

    connect(pMsgItem, &MsgItem::signal_selected, this, &ListTemplate::slot_selected);
    connect(pMsgItem, &MsgItem::signal_delete, this, &ListTemplate::slot_delete);
}

这边将MsgItem的创建操作放在函数体内是为了方便测试,通过函数参数的方式传递进来在实际使用过程中会比较方便。

选中效果的实现也比较简单,下面我们主要用一种方式实现,另一种方式在源代码中有,后面有链接。

//第一种选中的实现方式
void ListTemplate::slot_selected(MsgItem *pWidget)
{
    if(Q_NULLPTR == pWidget)
    {
        return;
    }
    if(Q_NULLPTR != m_pCurrentItem)
    {
         m_pCurrentItem->selected(false);
    }
	//先设置当前的为不选中即false状态,然后将点击的设置为选中状态,当前指针指向点击的item
    pWidget->selected(true);
    m_pCurrentItem = pWidget;
}

tem的删除也是比较简单的,但是在删除的时候别忘了资源回收。

void ListTemplate::slot_delete(QWidget* pWidget)
{
    delete pWidget;
    pWidget = Q_NULLPTR;
    QListWidgetItem *pItem = ui->listWidget->takeItem(ui->listWidget->currentRow());
    m_pCurrentItem = Q_NULLPTR;
    m_pCurrent = Q_NULLPTR;
}

这边有一个注意的点,假设信号槽的连接中没有参数,我们需要手动来获取加载在QListWidgetItem上的MsgItem,需要注意的是应该先使用iremWidget()来获取,再使用takeItem卸载QListWidgetItem,否则会出现取不到QWidget的情况。具体可以参考:
Qt 中 QListWidget 获取itemWidget() 失败
看下最终效果:
在这里插入图片描述
源码地址:https://gitee.com/Gqian_com/customcomponent

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章