はじめに
多くの場合、モデルを次のように構成する必要があります。1つの構造を持つモデルの同じレベルで、別のレベルでモデルの構造が変更されます。 たとえば、デバイスのリストを表示し、各デバイスに設定グループがあり、各設定グループにさまざまなタイプの設定のリストがあるタスクを考えます。 簡単にするために、デバイスには名前とグループのリストのみがあると仮定します。 グループには、名前と設定のリストのみがあります。 設定には、名前とタイプ(チェックボックス、テキストボックス、またはスライダー)のみがあります。
このパターンは、 記事に基づいて体系化されました。 以下は、GoFに似たパターンの説明です。
予定
QMLを使用したC ++での複雑なモデルの使用を構造化するパターン。 ネストされたモデルリストの使用を促進して、階層構造を形成します。 同時に、QMLで使用する場合、複雑さは増加しません。
適用性
次の場合にパターンを使用します。
- さまざまなレベルでさまざまなタイプのモデルが存在するモデルの階層を想像する必要があります
- モデルは動的に塗りつぶされます
構造
会員
- ListModel-リストモデルクラス。デリゲートはモデルのロールをListItemリストクラスに処理します。
- ListItem-リスト項目の抽象クラス。一意の識別子を見つけるためのメソッドとロールにアクセスするためのメソッドを定義する必要があります
- SubListedListModel-サブリストを持つリストモデルクラス;プロトタイプはSubListedListItemクラスのインターフェイスであり、現在の要素のサブモデルを返します。
- SubListedListItem-サブモデルを持つリストアイテムの抽象クラスは、そのサブモデルを定義する必要があります。
- ConcreteSubItem1 -ConcreteSubItem2要素のサブモデルリストを含むサブモデルを持つリストアイテムのクラス。
- ConcreteSubItem2 -ConcreteSubItem3項目のリストモデルを含むサブモデルを持つリスト項目クラス。
- ConcreteSubItem3はリスト項目クラスです。
関係
リストオブジェクトは、コンストラクターでプロトタイプとして指定されたリストアイテムのクラスにロールデータを委任します。 サブモデルを持つ要素から、または単純なリストアイテムから継承する必要があります。 QMLでは、子モデルにアクセスするには、subModelFromIdメソッドを呼び出す必要があります。ここで、パラメーターは現在の要素のidロールです。
C ++コード例
デバイスモデルを追加します。
class DeviceModelItem : public Models::SubListedListItem { Q_OBJECT public: enum GroupModelItemRoles { deviceId = Qt::UserRole + 1, deviceNameRole }; DeviceModelItem(QObject* parent = 0); int id() const; QVariant data(int role) const; QHash<int, QByteArray> roleNames() const; Models::ListModel* submodel() const; private: int _id; static int g_id; QString deviceName; Models::ListModel* groupListModel; };
識別子を追跡するには、グローバルカウンターg_idを使用します。現在のデバイスの識別子は_idです。 コンストラクターで、サブモデルを追加し、名前を初期化します。
DeviceModelItem::DeviceModelItem(QObject *parent):SubListedListItem(parent), _id(g_id++) { deviceName = QString("Device %1").arg(_id); groupListModel = new Models::SubListedListModel(new GroupModelItem()); // groupListModel->appendRow(new GroupModelItem()); groupListModel->appendRow(new GroupModelItem()); }
ロール処理:
QVariant DeviceModelItem::data(int role) const { switch (role) { case deviceId: return this->id(); case deviceNameRole: return this->deviceName; default: return QVariant(); } } QHash<int, QByteArray> DeviceModelItem::roleNames() const { QHash<int, QByteArray> roles; roles[deviceId] = "deviceId"; roles[deviceNameRole] = "deviceName"; return roles; }
グループのモデルは似ていますが、コンストラクターでサブモデルのないリストを作成する点が異なります。
GroupModelItem::GroupModelItem(QObject *parent):SubListedListItem(parent), _id(g_id++) { groupName = QString("Group %1").arg(_id); settingsListModel = new Models::ListModel(new SettingsModelItem()); ... }
モデルでは、設定はすでにListItemから継承されています。
class SettingsModelItem : public Models::ListItem { Q_OBJECT public: enum SettingsModelItemRoles { settingsId = Qt::UserRole + 1, settingsNameRole, settingsTypeRole } ... }
設定にサブモデルは必要ありません。 次に、ルートデバイスモデルをコンテキストに追加します。
int main(int argc, char *argv[]) { // touch QGuiApplication app(argc, argv); QQmlApplicationEngine engine; Models::ListModel* devicesModel = new Models::SubListedListModel(new DeviceModelItem()); //DEBUG devicesModel->appendRow(new DeviceModelItem()); devicesModel->appendRow(new DeviceModelItem()); devicesModel->appendRow(new DeviceModelItem()); devicesModel->appendRow(new DeviceModelItem()); devicesModel->appendRow(new DeviceModelItem()); engine.rootContext()->setContextProperty("deviceModel",devicesModel); ... }
QMLサンプルコード
ナビゲーションモデルとして、StackView(main.qml)を使用します。
StackView { id: stackView anchors.fill: parent initialItem: Item { width: parent.width height: parent.height ListView { model: deviceModel anchors.fill: parent delegate: AndroidDelegate { text: deviceName onClicked: stackView.push({item:Qt.resolvedUrl("pages/GroupPage.qml"), properties:{subModel:deviceModel.subModelFromId(model.deviceId)}}) } } } }
グループページの場合、subModelFromIdを介してサブモデルがインストールされました。 グループモデルでは、同じ方法で処理します。
ScrollView { ... property variant subModel: null ListView { ... model: subModel delegate: AndroidDelegate { text: groupName onClicked: stackView.push({item:Qt.resolvedUrl("SettingsPage.qml"), properties:{subModel:subModel.subModelFromId(model.groupId)}}) } } ... }
設定ページのみのリストの場合:
ListView { id: settingsView ... model: subModel delegate: Item { CheckBox{ visible: settingsType == 0 ... } Column{ ... visible: settingsType == 1 Text{text: settingsName} TextField {text: "Text input"} } Column{ ... visible: settingsType == 2 Text{text: settingsName} Slider {value: 1.0} } }
結果のスクリーンショット
ソースへのリンク: GitHub
ソース記事へのリンク: 記事