Android Studio プロジェクトで QtAbstractItemModel を使う
Qt Quick Android API のサンプルは Android Studio プロジェクト
Qt Quick for Android API のサンプルは Android Studio プロジェクトとして提供されています。プロジェクトフォルダは Qt のインストール場所にあります。
例えば、デフォルトの Windows インストールパスでは、ここにあります:
C:\Qt\Examples\Qt-<patch-release-number>\platforms\android\<example-name>
これらのプロジェクトは、Qtのバージョンと互換性のあるQt Gradle Pluginのバージョンを使用するように設定されています。
概要
この例では、Android Studioプロジェクト(qtabstractitemmodel_java)とQMLプロジェクト(qtabstractitemmodel)の2つのプロジェクトで構成されています。QMLプロジェクトをAndroidプロジェクトにインポートすることができます。
この例では、JavaとQMLの間で複雑なデータ型を扱う方法を示しています。QtAbstractItemModelと QtModelIndexのJava APIクラスの使い方を示しています。QML では、TableView アイテムを使ってデータの使い方を示します。Java では、行と列の入れ子になった ArrayList アイテムのモデルを使って、データの使い方を示します。QML がどのように動作するかについての詳細は Qt Qml.
例の実行
このサンプルを実行するには、Android StudioとQt Tools for Android Studioが必要です。Android Studio で qtabstractitemmodel_java を開き、Qt Tools for Android Studioの指示に従ってqtabstractitemmodel
をインポートしてください。
QML プロジェクト
QMLプロジェクト側では、Rectangle をルートオブジェクトとしています。dataModel
プロパティ変数には、Java 側で作成・配信されたデータモデルが格納されています。
Rectangle { id: mainRectangle property AbstractItemModel dataModel
TableView にはデータモデルが表示されます。
TableView { id: tableView model: mainRectangle.dataModel anchors {fill: parent; margins: 20} columnSpacing: 4 rowSpacing: 6 boundsBehavior: TableView.OvershootBounds clip: true ScrollBar.vertical: ScrollBar { policy: ScrollBar.AsNeeded } ScrollBar.horizontal: ScrollBar{ policy: ScrollBar.AsNeeded }
delegate
プロパティには、モデルの各セル項目がRectangle にTextEdit を含んで定義されています。TextEdit
の text プロパティはQAbstractItemModel::data() を使って設定され、与えられた role と index に基づいた値が返されます。
QMLからこれらのメソッドを呼び出すことは、QtのqtMainLoopThreadスレッドコンテキストで実行されることを意味します。
delegate: Rectangle { implicitWidth: (tableView.height > tableView.width) ? tableView.width / 10 : tableView.height / 5 implicitHeight: implicitWidth required property var model color: "#2CDE85" border {color: "#00414A"; width: 2} TextEdit { // Calls MyDataModel::data to get data based on the roles. // Called in Qt qtMainLoopThread thread context. // // After editing is finished, call MyDataModel::setData() // to update the value of selected cell. onEditingFinished: parent.model.edit = text text: parent.model.display font {pixelSize: 26; bold: true} padding: 5 anchors.fill: parent wrapMode: TextEdit.Wrap horizontalAlignment: TextEdit.AlignHCenter verticalAlignment: TextEdit.AlignVCenter } }
TextEdit フィールドを編集する場合、onEditingFinished()
ハンドラはモデルのedit
ロールの値を編集したテキストに設定します。これにより、QAbstractItemModel::setData ()メソッドが呼び出され、セルの編集されたテキストがモデルの対応するインデックスに更新されます。
詳細については、QAbstractItemModel を参照してください。
Android Studioプロジェクト
Android Studioプロジェクト(qtabstractitemmodel_java)には、Activityクラス(MainActivity
)とMyDataModel
クラスがあります。
データモデル
データモデルであるMyDataModel
は、QtAbstractItemModelクラスを継承しています。QtAbstractItemModel
はQAbstractItemModel のラッパーです。
MyDataModel
QtMainLoopThread と Android main thread の両方のスレッドコンテキストで実行されます。MyDataModel
クラスのメソッドでメンバ変数にアクセスする際には、同期を確保する必要があります。
まず、この例では単純な行と列のモックデータセットでモデルを初期化します。このコンストラクタ・メソッドはAndroidのメイン・スレッド・コンテキストで呼び出されることに注意してください。
/* * Initializes the two-dimensional array list with following content: * [] [] [] [] 1A 1B 1C 1D * [] [] [] [] 2A 2B 2C 2D * [] [] [] [] 3A 3B 3C 3D * [] [] [] [] 4A 4B 4C 4D * Threading: called in Android main thread context. */ public MyDataModel() {
この例では、QtAbstractItemModel のメソッドをオーバーライドしています。columnCount()メソッドとrowCount()メソッドは、モデル内のそれぞれのカウントを返します。各 rowCount() の実行は、Qt qtMainLoopThread と Android main thread の両方のスレッドコンテキストで行われます。
/* * Returns the count of columns. * Threading: called in Android main thread context. * Threading: called in Qt qtMainLoopThread thread context. */ @Override synchronized public int columnCount(QtModelIndex qtModelIndex) { return m_columns; } /* * Returns the count of rows. * Threading: called in Android main thread context. * Threading: called in Qt qtMainLoopThread thread context. */ @Override synchronized public int rowCount(QtModelIndex qtModelIndex) { return m_dataList.size(); }
data()メソッドは、JavaからQMLへ、ロールとインデックスに基づいたモデルデータを提供します。roleNames()メソッドは、ロールの数値とその名前を文字列でマッチングしたハッシュを返します。index()メソッドは、新しいモデルのインデックスを返します。parent()メソッドはインデックスの親を返すべきです。しかし、この例では親インデックスを持たないデータに焦点を当てているため、このメソッドをオーバーライドし、空の QtModelIndex() を返します。メソッドは QML から呼び出され、Qt の qtMainLoopThread スレッドコンテキストで実行されます。
/* * Returns the data to QML based on the roleNames * Threading: called in Qt qtMainLoopThread thread context. */ @Override synchronized public Object data(QtModelIndex qtModelIndex, int role) { if (role == ROLE_DISPLAY) { Cell elementForEdit = m_dataList.get(qtModelIndex.row()).get(qtModelIndex.column()); return elementForEdit.getValue(); } Log.w(TAG, "data(): unrecognized role: " + role); return null; } /* * Defines what string i.e. role in QML side gets the data from Java side. * Threading: called in Qt qtMainLoopThread thread context. */ @Override synchronized public HashMap<Integer, String> roleNames() { HashMap<Integer, String> roles = new HashMap<>(); roles.put(ROLE_DISPLAY, "display"); roles.put(ROLE_EDIT, "edit"); return roles; } /* * Returns a new index model. * Threading: called in Qt qtMainLoopThread thread context. */ @Override synchronized public QtModelIndex index(int row, int column, QtModelIndex parent) { return createIndex(row, column, 0); } /* * Returns a parent model. * Threading: not used called in this example. */ @Override synchronized public QtModelIndex parent(QtModelIndex qtModelIndex) { return new QtModelIndex(); }
この例では、QAbstractItemModel::setData()メソッドをオーバーライドしています。このメソッドは、index
のモデルのデータがアプリケーションの QML 側から設定される際に呼び出されます。
/* * Gets called when model data is edited from QML side. * Sets the role data for the item at index to value, * if given index is valid and if data in given index truly changed. */ @Override synchronized public boolean setData(QtModelIndex index, Object value, int role) { Cell cellAtIndex = m_dataList.get(index.row()).get(index.column()); String cellValueAtIndex = cellAtIndex.getValue(); if (!index.isValid() || role != ROLE_EDIT || Objects.equals(cellValueAtIndex, value.toString())) { return false; } cellAtIndex.setValue(value.toString()); // Send dataChanged() when data was successfully set. dataChanged(index, index, new int[]{role}); return true; }
この例では、MainActivity
UIインタラクションで行や列を追加したり削除したりするためのメソッドをモデル側に実装しています。beginInsertRow()のように、行の開始、終了、挿入、削除を呼び出し、モデルのインデックスを更新します。この例ではQtAbstractItemModel を使用しているため、モデルに新しい行を挿入するたびに beginInsertRows() と endInsertRows() を呼び出す必要があります。削除も同様です。これらのメソッドはAndroid側から呼び出されるため、実行はAndroidのメインスレッドコンテキストで行われます。
/* * Adds a row. * Threading: called in Android main thread context. */ synchronized public void addRow() { if (m_columns > 0 && m_dataList.size() < MAX_ROWS_AND_COLUMNS) { beginInsertRows(new QtModelIndex(), m_dataList.size(), m_dataList.size()); m_dataList.add(generateNewRow()); endInsertRows(); } } /* * Removes a row. * Threading: called in Android main thread context. */ synchronized public void removeRow() { if (m_dataList.size() > 1) { beginRemoveRows(new QtModelIndex(), m_dataList.size() - 1, m_dataList.size() - 1); m_dataList.remove(m_dataList.size() - 1); endRemoveRows(); } }
この例では、MainActivity
UIインタラクションで列の追加と削除を行うためのメソッドをモデル側に実装しています。beginRemoveColumn()のように、モデルのインデックスを更新するために、列の開始、終了、挿入、削除を呼び出します。行の追加と削除のメソッドと同じように、コンテキストの意識が適用されます。
/* * Adds a column. * Threading: called in Android main thread context. */ synchronized public void addColumn() { if (!m_dataList.isEmpty() && m_columns < MAX_ROWS_AND_COLUMNS) { beginInsertColumns(new QtModelIndex(), m_columns, m_columns); generateNewColumn(); m_columns += 1; endInsertColumns(); } } /* * Removes a column. * Threading: called in Android main thread context. */ synchronized public void removeColumn() { if (m_columns > 1) { int columnToRemove = m_columns - 1; beginRemoveColumns(new QtModelIndex(), columnToRemove, columnToRemove); for (int row = 0; row < m_dataList.size(); row++) m_dataList.get(row).remove(columnToRemove); m_columns -= 1; endRemoveColumns(); } }
メインアクティビティ
MainActivity
はQtQmlStatusChangeListenerインターフェースを実装し、QMLがロードされたときにステータスの更新を取得します。また、Androidのメインアクティビティでもあります。
この例では、データモデルを作成し、初期化しています。QtQuickViewも参照してください。
private final MyDataModel m_model = new MyDataModel();
この例では、UIボタンとそのリスナーを設定し、ユーザーがUIを介してモデルと対話できるようにしています。
/* * Returns the count of columns. * Threading: called in Android main thread context. * Threading: called in Qt qtMainLoopThread thread context. */ @Override synchronized public int columnCount(QtModelIndex qtModelIndex) { return m_columns; } /* * Returns the count of rows. * Threading: called in Android main thread context. * Threading: called in Qt qtMainLoopThread thread context. */ @Override synchronized public int rowCount(QtModelIndex qtModelIndex) { return m_dataList.size(); }
QMLコンテンツの読み込みを開始します。読み込みは、ready
のステータスが更新されるまで、バックグラウンドで行われます。
/* * Returns the data to QML based on the roleNames * Threading: called in Qt qtMainLoopThread thread context. */ @Override synchronized public Object data(QtModelIndex qtModelIndex, int role) { if (role == ROLE_DISPLAY) { Cell elementForEdit = m_dataList.get(qtModelIndex.row()).get(qtModelIndex.column()); return elementForEdit.getValue(); } Log.w(TAG, "data(): unrecognized role: " + role); return null; } /* * Defines what string i.e. role in QML side gets the data from Java side. * Threading: called in Qt qtMainLoopThread thread context. */ @Override synchronized public HashMap<Integer, String> roleNames() { HashMap<Integer, String> roles = new HashMap<>(); roles.put(ROLE_DISPLAY, "display"); roles.put(ROLE_EDIT, "edit"); return roles; } /* * Returns a new index model. * Threading: called in Qt qtMainLoopThread thread context. */ @Override synchronized public QtModelIndex index(int row, int column, QtModelIndex parent) { return createIndex(row, column, 0); } /* * Returns a parent model. * Threading: not used called in this example. */ @Override synchronized public QtModelIndex parent(QtModelIndex qtModelIndex) { return new QtModelIndex(); }
この例では、QML コンテンツがロードされ、ステータスが準備完了になると、データモデルを設定します。
/* * Gets called when model data is edited from QML side. * Sets the role data for the item at index to value, * if given index is valid and if data in given index truly changed. */ @Override synchronized public boolean setData(QtModelIndex index, Object value, int role) { Cell cellAtIndex = m_dataList.get(index.row()).get(index.column()); String cellValueAtIndex = cellAtIndex.getValue(); if (!index.isValid() || role != ROLE_EDIT || Objects.equals(cellValueAtIndex, value.toString())) { return false; } cellAtIndex.setValue(value.toString()); // Send dataChanged() when data was successfully set. dataChanged(index, index, new int[]{role}); return true; }
© 2025 The Qt Company Ltd. Documentation contributions included herein are the copyrights of their respective owners. The documentation provided herein is licensed under the terms of the GNU Free Documentation License version 1.3 as published by the Free Software Foundation. Qt and respective logos are trademarks of The Qt Company Ltd. in Finland and/or other countries worldwide. All other trademarks are property of their respective owners.