目錄
- 引言
- 相關閱讀
- 項目結構
- 文件組織
- 核心技術實現
- 1. 數據模型設計
- 聯系人項目類 (datamodel.h)
- 數據模型類 (datamodel.h)
- 數據模型實現 (datamodel.cpp)
- 2. 主程序入口點 (main.cpp)
- 3. 主界面設計 (Main.qml)
- 4. 聯系人對話框 (ContactDialog.qml)
- 5. 自定義組件
- CustomTextField.qml
- CustomButton.qml
- IconButton.qml
- 運行效果
- 總結
- 下載鏈接
引言
在上一篇中介紹了ListView的數據交互與樣式定制后,本文上一點強度,將通過一個聯系人管理的案例,詳細介紹如何使用QML與C++進行混合開發,充分展示QML的界面設計優勢和C++的數據處理能力。該應用基于ListView & Model實現了聯系人的增刪改查等基本功能,并通過自定義組件提升了用戶體驗。由于篇幅有限,會省略部分代碼,完整代碼請看本文最后的下載鏈接。
下一篇與ListView有關的文章,我將會進一步優化Model的性能。到時候在相關閱讀中補上鏈接。
相關閱讀
- 接上篇 —— QML ListView:列表視圖的數據交互與樣式定制
- 下篇 —— QML與C++:基于ListView調用外部模型進行增刪改查(性能優化版)
項目結構
以下是本項目的核心結構圖:
文件組織
qml_listview_cpp/
├── CMakeLists.txt # CMake構建配置
├── main.cpp # C++主函數
├── datamodel.h # 數據模型頭文件
├── datamodel.cpp # 數據模型實現
├── Main.qml # 主界面QML
├── ContactDialog.qml # 聯系人對話框QML
├── components/ # 自定義組件目錄
│ ├── CustomTextField.qml # 自定義文本輸入框
│ ├── CustomButton.qml # 自定義按鈕
│ └── IconButton.qml # 自定義圖標按鈕
├── icons/ # 圖標資源目錄
│ ├── user.png # 用戶圖標
│ ├── add.png # 添加圖標
│ ├── delete.png # 刪除圖標
│ ├── edit.png # 編輯圖標
│ ├── find.png # 搜索圖標
│ ├── phone.png # 電話圖標
│ └── clear.png # 清除圖標
└── image.qrc # Qt資源文件
核心技術實現
1. 數據模型設計
本項目采用了QAbstractListModel作為基類創建自定義數據模型,實現了聯系人數據的管理。C++的數據模型為QML提供了高效的數據源。
聯系人項目類 (datamodel.h)
class ContactItem {
public:ContactItem(const QString &name, const QString &phone): m_name(name), m_phone(phone) {}QString name() const { return m_name; }QString phone() const { return m_phone; }QString firstLetter() const { return m_name.isEmpty() ? "?" : m_name.left(1).toUpper(); }private:QString m_name;QString m_phone;
};
ContactItem
類定義了聯系人的基本屬性:姓名和電話。它還提供了一個便利方法firstLetter()
用于獲取姓名的首字母,這將用于UI中的頭像顯示。
數據模型類 (datamodel.h)
class DataModel : public QAbstractListModel
{Q_OBJECTpublic:enum Roles {NameRole = Qt::UserRole + 1,PhoneRole,FirstLetterRole};explicit DataModel(QObject *parent = nullptr);int rowCount(const QModelIndex &parent = QModelIndex()) const override;QVariant data(const QModelIndex &index, int role = Qt::DisplayRole) const override;QHash<int, QByteArray> roleNames() const override;// 暴露給QML的方法Q_INVOKABLE bool addContact(const QString &name, const QString &phone);Q_INVOKABLE bool removeContact(int index);Q_INVOKABLE bool editContact(int index, const QString &name, const QString &phone);Q_INVOKABLE QVariantList searchContacts(const QString &keyword);Q_INVOKABLE void clearSearch();private:QList<ContactItem> m_items;QList<ContactItem> m_originalItems; // 用于存儲搜索前的原始數據
};
DataModel
類繼承自QAbstractListModel
,實現了必要的虛函數:
rowCount()
: 返回模型中的項目數量data()
: 根據索引和角色返回項目數據roleNames()
: 定義了模型中可用的角色名稱,這些名稱將在QML中使用
此外,還通過Q_INVOKABLE
宏定義了幾個可以從QML中直接調用的方法:
addContact()
: 添加聯系人removeContact()
: 刪除聯系人editContact()
: 編輯聯系人searchContacts()
: 搜索聯系人clearSearch()
: 清除搜索,恢復原始列表
數據模型實現 (datamodel.cpp)
數據模型的核心實現如下:
DataModel::DataModel(QObject *parent): QAbstractListModel(parent)
{// 添加一些示例聯系人數據m_items.append(ContactItem("張三", "13800138000"));m_items.append(ContactItem("李四", "13900139000"));m_items.append(ContactItem("王五", "13700137000"));// 保存原始數據m_originalItems = m_items;
}QVariant DataModel::data(const QModelIndex &index, int role) const
{if (!index.isValid())return QVariant();if (index.row() >= m_items.count())return QVariant();const ContactItem &item = m_items.at(index.row());switch (role) {case NameRole:return item.name();case PhoneRole:return item.phone();case FirstLetterRole:return item.firstLetter();default:return QVariant();}
}// 搜索聯系人實現
QVariantList DataModel::searchContacts(const QString &keyword)
{if (keyword.isEmpty()) {beginResetModel();m_items = m_originalItems;endResetModel();return QVariantList();}QVariantList results;beginResetModel();m_items.clear();for (const ContactItem &item : m_originalItems) {if (item.name().contains(keyword, Qt::CaseInsensitive) ||item.phone().contains(keyword, Qt::CaseInsensitive)) {m_items.append(item);}}endResetModel();return results;
}
2. 主程序入口點 (main.cpp)
主函數設置了QML引擎并將C++數據模型暴露給QML:
#include <QGuiApplication>
#include <QQmlApplicationEngine>
#include <QQmlContext>
#include "datamodel.h"int main(int argc, char *argv[])
{QGuiApplication app(argc, argv);QQmlApplicationEngine engine;QObject::connect(&engine,&QQmlApplicationEngine::objectCreationFailed,&app,[]() { QCoreApplication::exit(-1); },Qt::QueuedConnection);// 創建數據模型實例DataModel *model = new DataModel(&engine);// 將模型暴露給QMLengine.rootContext()->setContextProperty("dataModel", model);engine.loadFromModule("qml_listview_cpp", "Main");return app.exec();
}
通過setContextProperty
方法,將C++數據模型注冊為QML上下文屬性,這樣在QML代碼中就可以直接訪問dataModel
對象了。
3. 主界面設計 (Main.qml)
主界面采用了QML編寫,實現了聯系人的列表顯示和搜索功能:
import QtQuick
import QtQuick.Window
import QtQuick.Controls
import QtQuick.Layouts
import "./components" // 導入自定義組件Window {width: 640height: 480visible: truetitle: "聯系人列表"// ... 省略部分代碼 ...ColumnLayout {anchors.fill: parentanchors.margins: 10spacing: 10// 頂部工具欄Rectangle {Layout.fillWidth: trueheight: 50color: "#f0f0f0"radius: 5RowLayout {anchors.fill: parentanchors.margins: 5spacing: 10CustomTextField {id: searchFieldLayout.fillWidth: trueplaceholderText: "搜索聯系人..."leftIcon: "qrc:/icons/find.png"onTextChanged: dataModel.searchContacts(text)onRightIconClicked: {text = ""dataModel.clearSearch()}}IconButton {text: "添加聯系人"iconSource: "qrc:/icons/add.png"showBackground: truebackgroundColor: "#BBDEFB"onClicked: addContactDialog.open()}}}// 聯系人列表ListView {Layout.fillWidth: trueLayout.fillHeight: truemodel: dataModelspacing: 10clip: truedelegate: Rectangle {width: ListView.view.widthheight: 80color: "#f0f0f0"radius: 5// ... 省略部分代碼 ...RowLayout {anchors.fill: parentanchors.margins: 10spacing: 15// 首字母頭像Rectangle {width: 60height: 60radius: width / 2color: {const colors = ["#FF6B6B", "#4ECDC4", "#45B7D1", "#96CEB4", "#FFEEAD", "#D4A5A5", "#9B59B6"]return colors[firstLetter.charCodeAt(0) % colors.length]}Text {anchors.centerIn: parenttext: firstLettercolor: "white"font.pixelSize: 24font.bold: true}}// 聯系人信息ColumnLayout {Layout.fillWidth: truespacing: 5Text {text: namefont.bold: truefont.pixelSize: 16Layout.fillWidth: true}Text {text: phonecolor: "#666666"font.pixelSize: 14Layout.fillWidth: true}}// 操作按鈕RowLayout {spacing: 10// 編輯按鈕IconButton {iconSource: "qrc:/icons/edit.png"onClicked: {currentEditIndex = indexeditContactDialog.currentName = nameeditContactDialog.currentPhone = phoneeditContactDialog.open()}}// 刪除按鈕IconButton {iconSource: "qrc:/icons/delete.png"onClicked: dataModel.removeContact(index)}}}}}}
}
主界面的核心部分是一個ListView,它使用C++提供的dataModel作為數據源。每個聯系人項目都顯示為一個帶有圓形首字母頭像、姓名、電話號碼以及編輯和刪除按鈕的矩形卡片。
主界面效果圖:
4. 聯系人對話框 (ContactDialog.qml)
為了添加和編輯聯系人,項目實現了一個模態對話框:
import QtQuick
import QtQuick.Controls
import QtQuick.Layouts
import "./components"Dialog {id: rootwidth: 400modal: true// 屬性property bool isEdit: false // 用于區分是編輯還是添加模式property string currentName: ""property string currentPhone: ""// 標題根據模式動態設置title: isEdit ? "修改聯系人" : "添加新聯系人"closePolicy: Dialog.NoAutoClose// 信號signal contactConfirmed(string name, string phone)// ... 省略部分代碼 ...contentItem: ColumnLayout {spacing: 20anchors.margins: 10CustomTextField {id: nameFieldLayout.fillWidth: trueplaceholderText: "姓名"leftIcon: "qrc:/icons/user.png"}CustomTextField {id: phoneFieldLayout.fillWidth: trueplaceholderText: "電話"leftIcon: "qrc:/icons/phone.png"validator: RegularExpressionValidator {regularExpression: /^[0-9\+\-\s]*$/}}// 按鈕區域RowLayout {Layout.alignment: Qt.AlignRight | Qt.AlignBottomspacing: 10CustomButton {id: confirmButtontext: isEdit ? qsTr("保存") : qsTr("確定")enabled: nameField.text.length > 0 && phoneField.text.length > 0onClicked: root.accept()}CustomButton {id: cancelButtontext: qsTr("取消")bgColor: "#f5f5f5"textColor: "#333333"onClicked: root.reject()}}}
}
這個對話框可以在兩種模式下工作:添加新聯系人和編輯現有聯系人。它包含兩個自定義文本輸入字段用于輸入姓名和電話號碼,以及確認和取消按鈕。
對話框效果圖:
5. 自定義組件
為了提升UI的美觀度和復用性,項目定義了幾個自定義組件:
CustomTextField.qml
此處代碼省略…
這個自定義文本輸入框增強了標準的TextField,添加了左側圖標、右側圖標或清除按鈕等功能。如果所示:
CustomButton.qml
此處代碼省略…
自定義按鈕組件提供了更靈活的外觀定制,包括背景色、文本色以及懸停效果。
主要在對話窗中使用了CustomButton:
IconButton.qml
此處代碼省略…
圖標按鈕組件實現了一個可以顯示圖標和文本的自定義按鈕,提供了豐富的自定義選項,如圖標大小、邊框、背景色等。
在列表中使用了IconButton:
在添加聯系人按鈕上使用了IconButton:
只需要設置背景色和文字即可實現不同的樣式效果。
運行效果
查找聯系人:
修改聯系人:
新增/刪除聯系人:
總結
本文介紹了一個基于Qt/QML與C++混合開發的聯系人管理應用。通過這個示例,我們展示了:
- QML與C++協同工作的模式:QML負責直觀高效的UI設計,C++處理數據邏輯和模型。
- 自定義QML組件的實現:通過組件化設計提高代碼復用性和可維護性。
- QAbstractListModel的使用:通過繼承QAbstractListModel創建自定義數據模型。
- 信號與槽機制:利用Qt的信號與槽機制實現UI與數據模型的解耦。
下載鏈接
您可以通過以下鏈接獲取完整的源代碼:GitCode -> QML -> ListView & Model