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

  • 2019 年 10 月 7 日
  • 筆記

一、前言

前面幾篇文章中的控件基本上難度係數接近0,甚至有湊控件數量的嫌疑,這次必須來一個強悍的控件,本控件難度係數在所有控件中排前五,代碼量也不少,頭文件都550行,實現文件1600行,為什麼這麼多呢,其實本控件是由好多個子控件組成的,字母高亮背景類、中間字母分隔類、右側字母導航類、通訊錄按鈕類、自定義滾動條類,我在寫比較複雜的控件的時候,一般都會逐個功能拆分,然後思考是否該功能可以做成獨立的類,這樣管理起來比較方便,也方便查看代碼。

最開始拿到這個控件需求的時候,也覺得不會簡單,要求用純QWidget實現,qml實現滑動等各種效果很方便,天生的優勢,而QWidget就需要自己來實現了,需求主要是要求五點,能夠批量和單個添加聯繫人信息(頭像+姓名+標識)、能夠滑動列表懸浮滾動條、能夠自動按照字母分類、提供字母導航欄直接快速定位、單擊聯繫人發出對應聯繫人的詳細信息。

二、實現的功能

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

三、效果圖

在這裡插入圖片描述

四、頭文件代碼

#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);  };

五、核心代碼

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);  }

六、控件介紹

  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. 強烈推薦程序員自我修養和規劃系列書《大話程序員》《程序員的成長課》《解憂程序員》,受益匪淺,受益終生!