Android Studio プロジェクトでの QtAbstractItemModel の使用
Qt Quick Android API のサンプルは Android Studio プロジェクト形式です
Android API のQt Quick に関するサンプルは、Android Studioプロジェクトとして提供されています。プロジェクトフォルダは、Qtのインストール先にあります。
たとえば、Windowsのデフォルトのインストールパスでは、以下の場所に配置されています:
C:\Qt\Examples\Qt-<patch-release-number>\platforms\android\<example-name>これらのプロジェクトは、この Qt バージョンと互換性のあるQt Gradle プラグインのバージョンを使用するように、すでに設定されています。
概要

このサンプルは、Android Studio プロジェクト(qtabstractitemmodel_java)と QML プロジェクト(qtabstractitemmodel)の 2 つのプロジェクトで構成されています。QML プロジェクトを Android プロジェクトにインポートすることができます。
このサンプルでは、JavaとQMLの間で複雑なデータ型を扱う方法を示しています。また、QtAbstractItemModelおよび QtModelIndexといった JavaAPIクラスの使用方法についても解説しています。 QML では、TableView アイテムを使用してデータの使用方法が示されています。Java では、行と列に対してネストされた ArrayList アイテムのモデルを使用して、データの使用方法が示されています。QML の仕組みに関する詳細については、以下を参照してください。 Qt Qmlを参照してください。
例の動作
この例を実行するには、標準の Qt for Android インストールに加え、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 dataModelTableView には、データモデルが表示されます。
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 のプロパティでは、モデルの各セル項目が、TextEdit を含むRectangle で定義されています。TextEdit のtextプロパティは、指定されたroleとindexに基づいて値を返すQAbstractItemModel::data()を使用して設定されます。
Qt 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) には、1 つの Activity クラス `MainActivity ` と `MyDataModel ` クラスが含まれています。
データモデル
データモデルであるMyDataModel は、QtAbstractItemModelクラスを継承しています。QtAbstractItemModel は、QAbstractItemModel のラッパーです。
MyDataModel クラスのメソッドはQML側とAndroid側の両方から呼び出されるため、実行はQtのqtMainLoopThreadおよびAndroidのメインスレッドという両方のスレッドコンテキストで行われます。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 のメインスレッドという、両方のスレッドコンテキストで行われます。
/*
* 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() メソッドは、数値のロール値と文字列としての名前を対応付けるハッシュを返します。QML では、これらのロール名を使用してモデルから対応するデータを取得します。 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() メソッドをオーバーライドしています。このメソッドは、アプリケーションの QML 側からモデル内のindex にあるデータが設定された際に呼び出されます。
/*
* 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 は、QMLの読み込み時にステータス更新を取得するために、QtQmlStatusChangeListenerインターフェースを実装しています。これは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コンテンツの読み込みが完了し、ステータスが「ready」になった時点で、データモデルを設定します。
/*
* 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;
}© 2026 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.