Android Studio プロジェクトで QtAbstractItemModel を使う
Qt Quick for Android API のサンプルは Android Studio プロジェクトとして提供されています。プロジェクトのフォルダは Qt のインストール場所にあります。
例えば、デフォルトの Windows インストールパスでは、ここにあります:
C:\Qt\Examples\Qt-/1\platforms\android
概要
このサンプルは2つのプロジェクトで構成されています:Android Studioプロジェクト(qtabstractitemmodel_java)とQMLプロジェクト(qtabstractitemmodel)です。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メインスレッドコンテキストの両方のスレッドコンテキストで発生します。
/* * 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
QMLがロードされたときにステータスの更新を取得するために、 インターフェースを実装しています。また、Androidのメインアクティビティでもあります。QtQmlStatusChangeListener
この例では、データモデルを作成し、初期化しています。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.