Qt編寫自定義控件55-手機通信錄

1、前言

前面幾篇文章中的控件基本上難度係數接近0,甚至有湊控件數量的嫌疑,此次必須來一個強悍的控件,本控件難度係數在全部控件中排前五,代碼量也很多,頭文件都550行,實現文件1600行,爲何這麼多呢,其實本控件是由好多個子控件組成的,字母高亮背景類、中間字母分隔類、右側字母導航類、通信錄按鈕類、自定義滾動條類,我在寫比較複雜的控件的時候,通常都會逐個功能拆分,而後思考是否該功能能夠作成獨立的類,這樣管理起來比較方便,也方便查看代碼。 最開始拿到這個控件需求的時候,也以爲不會簡單,要求用純QWidget實現,qml實現滑動等各類效果很方便,天生的優點,而QWidget就須要本身來實現了,需求主要是要求五點,可以批量和單個添加聯繫人信息(頭像+姓名+標識)、可以滑動列表懸浮滾動條、可以自動按照字母分類、提供字母導航欄直接快速定位、單擊聯繫人發出對應聯繫人的詳細信息。linux

2、實現的功能

  • 1:可設置信息集合(圖標+姓名+類型+電話)以及添加單個聯繫人
  • 2:可設置背景圖片+背景顏色
  • 3:可設置右側導航字母的列表+默認顏色+高亮顏色
  • 4:可設置聯繫人按鈕姓名顏色+姓名字體
  • 5:可設置聯繫人按鈕類型顏色+姓名字體
  • 6:可設置聯繫人按鈕選中背景顏色
  • 7:可設置字母導航的風格(背景顏色+線條)
  • 8:可設置字母導航的顏色+字體大小
  • 9:可設置各類邊距+聯繫人列數+元素間隔等
  • 10:支持懸浮滾動條,可設置懸停時間
  • 11:可設置懸浮滾動條的正常顏色+高亮顏色
  • 12:支持滑動,可設置滑動的步長速度
  • 13:支持單擊右側字母導航定位+文本突出顯示
  • 14:單擊發出當前聯繫人的姓名+類型+電話等信息
  • 15:根據漢字字母排序從小到大排列聯繫人,自帶漢字轉拼音功能

3、效果圖

4、頭文件代碼

#ifdef quc
#if (QT_VERSION < QT_VERSION_CHECK(5,7,0))
#include <QtDesigner/QDesignerExportWidget>
#else
#include <QtUiPlugin/QDesignerExportWidget>
#endif

class QDESIGNER_WIDGET_EXPORT TelWidget : public QWidget
#else
class TelWidget : public QWidget
#endif

{
    Q_OBJECT
    Q_PROPERTY(QString names READ getNames WRITE setNames)
    Q_PROPERTY(QString types READ getTypes WRITE setTypes)
    Q_PROPERTY(QString tels READ getTels WRITE setTels)

    Q_PROPERTY(QPixmap bgImage READ getBgImage WRITE setBgImage)
    Q_PROPERTY(QColor bgColor READ getBgColor WRITE setBgColor)

    Q_PROPERTY(int telHighFontSize READ getTelHighFontSize WRITE setTelHighFontSize)
    Q_PROPERTY(QPixmap telHighBgImage READ getTelHighBgImage WRITE setTelHighBgImage)
    Q_PROPERTY(QColor telHighBgColor READ getTelHighBgColor WRITE setTelHighBgColor)
    Q_PROPERTY(QColor telHighTextColor READ getTelHighTextColor WRITE setTelHighTextColor)

    Q_PROPERTY(QColor telBannerBgColor READ getTelBannerBgColor WRITE setTelBannerBgColor)
    Q_PROPERTY(QColor telBannerTextColor READ getTelBannerTextColor WRITE setTelBannerTextColor)
    Q_PROPERTY(QColor telBannerLineColor READ getTelBannerLineColor WRITE setTelBannerLineColor)

    Q_PROPERTY(QColor telLetterNormalColor READ getTelLetterNormalColor WRITE setTelLetterNormalColor)
    Q_PROPERTY(QColor telLetterHighColor READ getTelLetterHighColor WRITE setTelLetterHighColor)

    Q_PROPERTY(QColor telButtonBgColor READ getTelButtonBgColor WRITE setTelButtonBgColor)
    Q_PROPERTY(QColor telButtonNameColor READ getTelButtonNameColor WRITE setTelButtonNameColor)
    Q_PROPERTY(QColor telButtonTypeColor READ getTelButtonTypeColor WRITE setTelButtonTypeColor)

    Q_PROPERTY(QColor telPanelNormalColor READ getTelPanelNormalColor WRITE setTelPanelNormalColor)
    Q_PROPERTY(QColor telPanelHighColor READ getTelPanelHighColor WRITE setTelPanelHighColor)

public:
    //聯繫人結構體
    struct TelInfo {
        QString letter;
        QString name;
        QString type;
        QString tel;
        QPixmap pixmap;

        bool operator <(const TelInfo &telInfo)const
        {
            return letter < telInfo.letter;
            //return (letter == "#") ? false : (letter < telInfo.letter);
        }

        bool operator >(const TelInfo &telInfo)const
        {
            return letter > telInfo.letter;
            //return (letter == "#") ? true : (letter > telInfo.letter);
        }
    };

    explicit TelWidget(QWidget *parent = 0);
    ~TelWidget();

protected:
    void resizeEvent(QResizeEvent *);
    void showEvent(QShowEvent *);
    void paintEvent(QPaintEvent *);

private:
    QString names;                  //姓名集合
    QString types;                  //類型集合
    QString tels;                   //電話集合

    QPixmap bgImage;                //背景圖片
    QColor bgColor;                 //背景顏色

    int telHighFontSize;            //高亮標籤字體大小
    QPixmap telHighBgImage;         //高亮標籤背景圖片
    QColor telHighBgColor;          //高亮標籤背景顏色
    QColor telHighTextColor;        //高亮標籤文字顏色

    QColor telBannerBgColor;        //頂部字母導航背景顏色
    QColor telBannerTextColor;      //頂部字母導航文字顏色
    QColor telBannerLineColor;      //頂部字母導航線條顏色

    QColor telLetterNormalColor;    //右側字母導航正常顏色
    QColor telLetterHighColor;      //右側字母導航高亮顏色

    QColor telButtonBgColor;        //通信錄按鈕背景顏色
    QColor telButtonNameColor;      //通信錄按鈕姓名顏色
    QColor telButtonTypeColor;      //通信錄按鈕類型顏色

    QColor telPanelNormalColor;     //滾動條正常顏色
    QColor telPanelHighColor;       //滾動條高亮顏色

    int lastPosition;               //最後滾動條位置
    TelHigh *telHigh;               //高亮字母標籤
    TelBanner *telBanner;           //頂部間隔字母導航
    TelLetter *telLetter;           //右側字母標籤
    TelPanel *telPanel;             //通信錄面板

    QList<QWidget *> items;         //通信錄按鈕集合
    QList<QWidget *> banners;       //通信錄字母分割集合
    QList<QPixmap> pixmaps;         //聯繫人圖片集合
    QTimer *timer;                  //隱藏高亮標籤訂時器

public:
    QString getNames()              const;
    QString getTypes()              const;
    QString getTels()               const;

    QPixmap getBgImage()            const;
    QColor getBgColor()             const;

    int getTelHighFontSize()        const;
    QPixmap getTelHighBgImage()     const;
    QColor getTelHighBgColor()      const;
    QColor getTelHighTextColor()    const;

    QColor getTelBannerBgColor()    const;
    QColor getTelBannerTextColor()  const;
    QColor getTelBannerLineColor()  const;

    QColor getTelLetterNormalColor()const;
    QColor getTelLetterHighColor()  const;

    QColor getTelButtonBgColor()    const;
    QColor getTelButtonNameColor()  const;
    QColor getTelButtonTypeColor()  const;

    QColor getTelPanelNormalColor() const;
    QColor getTelPanelHighColor()   const;

    QSize sizeHint()                const;
    QSize minimumSizeHint()         const;

private slots:
    void initControl();
    void initForm();
    void btnPressed();
    void btnRelease();
    void positionChanged(int value);
    void letterClicked(const QString &letter, int letterY);

public Q_SLOTS:
    //設置姓名+類型+電話集合
    void setNames(const QString &names);
    void setTypes(const QString &types);
    void setTels(const QString &tels);
    void setInfo(const QString &names, const QString &types, const QString &tels);
    void setInfo(const QStringList &names, const QStringList &types,
                 const QStringList &tels, const QList<QPixmap> &pixmaps);

    //添加單個聯繫人
    void addInfo(const QString &name, const QString &type,
                 const QString &tel, const QPixmap &pixmap);

    //設置背景圖+背景顏色
    void setBgImage(const QPixmap &bgImage);
    void setBgColor(const QColor &bgColor);

    //設置高亮字母標籤相關屬性
    void setTelHighFontSize(int telHighFontSize);
    void setTelHighBgImage(const QPixmap &telHighBgImage);
    void setTelHighBgColor(const QColor &telHighBgColor);
    void setTelHighTextColor(const QColor &telHighTextColor);

    //設置頂部字母導航相關屬性
    void setTelBannerBgColor(const QColor &telBannerBgColor);
    void setTelBannerTextColor(const QColor &telBannerTextColor);
    void setTelBannerLineColor(const QColor &telBannerLineColor);

    //設置右側字母導航相關屬性
    void setTelLetterNormalColor(const QColor &telLetterNormalColor);
    void setTelLetterHighColor(const QColor &telLetterHighColor);

    //設置通信錄按鈕相關屬性
    void setTelButtonBgColor(const QColor &telButtonBgColor);
    void setTelButtonNameColor(const QColor &telButtonNameColor);
    void setTelButtonTypeColor(const QColor &telButtonTypeColor);

    //設置滾動條相關屬性
    void setTelPanelNormalColor(const QColor &telPanelNormalColor);
    void setTelPanelHighColor(const QColor &telPanelHighColor);

Q_SIGNALS:
    void telClicked(const QString &name, const QString &type, const QString &tel);
};

5、核心代碼

void TelWidget::setInfo(const QStringList &names, const QStringList &types,
                        const QStringList &tels, const QList<QPixmap> &pixmaps)
{
    this->names = names.join("|");
    this->types = types.join("|");
    this->tels = tels.join("|");
    this->pixmaps = pixmaps;

    if (names.isEmpty() || types.isEmpty() || tels.isEmpty()) {
        return;
    }

    //行標識符文字集合
    QList<QString> texts;
    texts << "A" << "B" << "C" << "D" << "E" << "F" << "G" << "H" << "I" << "J" << "K" << "L" << "M"
          << "N" << "O" << "P" << "Q" << "R" << "S" << "T" << "U" << "V" << "W" << "X" << "Y" << "Z" << "#";

    QList<QString> listName = names;
    QList<QString> listType = types;
    QList<QString> listTel = tels;
    QList<QPixmap> listPix = pixmaps;
    int countName = listName.count();
    int countType = listType.count();
    int countTel = listTel.count();
    int countPix = listPix.count();

    if (countName == countType && countType == countTel && countTel == countPix) {
        //取出對應漢字首字母,先對全部姓名按照字母從小到大排序
        QList<TelInfo> poundInfos, telInfos;
        for (int i = 0; i < countName; i++) {
            TelInfo telInfo;
            telInfo.name = listName.at(i);
            telInfo.type = listType.at(i);
            telInfo.tel = listTel.at(i);
            telInfo.pixmap = listPix.at(i);

            //若是首字母未找到字母則歸結到 # 分類中
            QString letter = ZhToPY::Instance()->zhToZM(listName.at(i).at(0));
            if (texts.contains(letter)) {
                telInfo.letter = ZhToPY::Instance()->zhToJP(listName.at(i));
                telInfos << telInfo;
            } else {
                telInfo.letter = "#";
                poundInfos << telInfo;
            }
        }

        //對信息集合進行升序排序
        qSort(telInfos.begin(), telInfos.end());

        //對最後的 # 類別追加到末尾
        foreach (TelInfo telInfo, poundInfos) {
            telInfos << telInfo;
        }

        //先要清空全部元素
        qDeleteAll(items);
        qDeleteAll(banners);
        items.clear();
        banners.clear();

        //生成電話本按鈕
        for (int i = 0; i < countName; i++) {
            TelInfo telInfo = telInfos.at(i);
            TelButton *btn = new TelButton;
            connect(btn, SIGNAL(btnPressed()), this, SLOT(btnPressed()));
            connect(btn, SIGNAL(btnRelease()), this, SLOT(btnRelease()));

            //設置字母屬性
            QString letter = telInfo.letter.at(0);
            btn->setProperty("letter", letter);

            //設置頭像+姓名+類型+電話
            btn->setPixmap(telInfo.pixmap);
            btn->setName(telInfo.name);
            btn->setType(telInfo.type);
            btn->setTel(telInfo.tel);
            items << btn;
        }

        //逐個計算字母對應的索引
        QList<int> tempIndex;
        int textCount = texts.count();
        for (int j = 0; j < textCount; j++) {
            QString text = texts.at(j);
            int index = -1;
            for (int k = 0; k < items.count(); k++) {
                if (items.at(k)->property("letter").toString() == text) {
                    index = k;
                    break;
                }
            }

            tempIndex << index;
        }

        //過濾索引,標識符索引>=0纔算數
        QList<int> indexs;
        for (int j = 0; j < textCount; j++) {
            int index = tempIndex.at(j);
            if (index >= 0) {
                TelBanner *banner = new TelBanner;
                banner->setText(texts.at(j));
                banners << banner;
                indexs << index;
            }
        }

        //設置標識符+元素集合
        telPanel->setIndexs(indexs);
        telPanel->setBanners(banners);
        telPanel->setItems(items);

        //從新設置顏色
        setTelHighBgColor(telHighBgColor);
        setTelBannerBgColor(telBannerBgColor);
        setTelBannerTextColor(telBannerTextColor);
        setTelLetterNormalColor(telLetterNormalColor);
        setTelLetterHighColor(telLetterHighColor);
        setTelButtonNameColor(telButtonNameColor);
        setTelButtonTypeColor(telButtonTypeColor);
        setTelPanelNormalColor(telPanelNormalColor);
        setTelPanelHighColor(telPanelHighColor);
    }
}

void TelWidget::addInfo(const QString &name, const QString &type,
                        const QString &tel, const QPixmap &pixmap)
{
    names = names + "|" + name;
    types = types + "|" + type;
    tels = tels + "|" + tel;
    pixmaps.append(pixmap);
    setInfo(names.split("|"), types.split("|"), tels.split("|"), pixmaps);
}

6、控件介紹

  1. 超過150個精美控件,涵蓋了各類儀表盤、進度條、進度球、指南針、曲線圖、標尺、溫度計、導航條、導航欄,flatui、高亮按鈕、滑動選擇器、農曆等。遠超qwt集成的控件數量。
  2. 每一個類均可以獨立成一個單獨的控件,零耦合,每一個控件一個頭文件和一個實現文件,不依賴其餘文件,方便單個控件以源碼形式集成到項目中,較少代碼量。qwt的控件類環環相扣,高度耦合,想要使用其中一個控件,必須包含全部的代碼。
  3. 所有純Qt編寫,QWidget+QPainter繪製,支持Qt4.6到Qt5.13的任何Qt版本,支持mingw、msvc、gcc等編譯器,支持任意操做系統好比windows+linux+mac+嵌入式linux等,不亂碼,可直接集成到Qt Creator中,和自帶的控件同樣使用,大部分效果只要設置幾個屬性便可,極爲方便。
  4. 每一個控件都有一個對應的單獨的包含該控件源碼的DEMO,方便參考使用。同時還提供一個全部控件使用的集成的DEMO。
  5. 每一個控件的源代碼都有詳細中文註釋,都按照統一設計規範編寫,方便學習自定義控件的編寫。
  6. 每一個控件默認配色和demo對應的配色都很是精美。
  7. 超過130個可見控件,6個不可見控件。
  8. 部分控件提供多種樣式風格選擇,多種指示器樣式選擇。
  9. 全部控件自適應窗體拉伸變化。
  10. 集成自定義控件屬性設計器,支持拖曳設計,所見即所得,支持導入導出xml格式。
  11. 自帶activex控件demo,全部控件能夠直接運行在ie瀏覽器中。
  12. 集成fontawesome圖形字體+阿里巴巴iconfont收藏的幾百個圖形字體,享受圖形字體帶來的樂趣。
  13. 全部控件最後生成一個動態庫文件(dll或者so等),能夠直接集成到qtcreator中拖曳設計使用。
  14. 目前已經有qml版本,後期會考慮出pyqt版本,若是用戶需求量很大的話。
  15. 自定義控件插件開放動態庫使用(永久免費),無任何後門和限制,請放心使用。
  16. 目前已提供26個版本的dll,其中包括了qt5.12.3 msvc2017 32+64 mingw 32+64 的。
  17. 不按期增長控件和完善控件,不按期更新SDK,歡迎各位提出建議,謝謝!
  18. Qt入門書籍推薦霍亞飛的《Qt Creator快速入門》《Qt5編程入門》,Qt進階書籍推薦官方的《C++ GUI Qt4編程》。
  19. 強烈推薦程序員自我修養和規劃系列書《大話程序員》《程序員的成長課》《解憂程序員》,受益不淺,受益終生!
  20. SDK下載連接:https://pan.baidu.com/s/1A5Gd77kExm8Co5ckT51vvQ 提取碼:877p
相關文章
相關標籤/搜索