Android StudioプロジェクトでQtAbstractItemModelを使う
概要
この例では、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 プロジェクト
QMLプロジェクト側では、Rectangle をルートオブジェクトとしています。dataModel
プロパティ変数には、Java 側で作成・配信されたデータモデルが格納されています。
Rectangle { id: mainRectangle property AbstractItemModel dataModel
TableView にはデータモデルが表示されます。 プロパティでは、モデルの各セル項目を で定義しています。delegate
Text
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 }
デリゲートでは、モデルのQHash<int, QByteArray> QAbstractItemModel::roleNames() const 、QtModelIndex index(int row, int column, QtModelIndex parent) 、Object data(QtModelIndex qtModelIndex, int role) メソッドを通して、row
、column
プロパティを設定しています。
QMLからこれらのメソッドを呼び出すことは、QtのqtMainLoopThreadスレッドコンテキストで実行されることを意味します。
詳しい説明はQAbstractItemModel を参照してください。
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 }
Android Studioプロジェクト
Android Studioプロジェクト(qtabstractitemmodel_java)には、ActivityクラスMainActivity
とMyDataModel
があります。
データモデル
データモデルであるc 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()メソッドは、ロールの数値とその名前を文字列でマッチングしたハッシュを返します。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) { switch (role) { case ROLE_ROW: Cell elementForRow = m_dataList.get(qtModelIndex.row()).get(qtModelIndex.column()); String row = String.valueOf(elementForRow.getRow()); return row; case ROLE_COLUMN: Cell elementForColumn = m_dataList.get(qtModelIndex.row()).get(qtModelIndex.column()); String column = elementForColumn.getColumn(); return column; default: 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_ROW, "row"); roles.put(ROLE_COLUMN, "column"); 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(); }
この例では、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(generateRow()); 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); generateColumn(); 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); 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) { switch (role) { case ROLE_ROW: Cell elementForRow = m_dataList.get(qtModelIndex.row()).get(qtModelIndex.column()); String row = String.valueOf(elementForRow.getRow()); return row; case ROLE_COLUMN: Cell elementForColumn = m_dataList.get(qtModelIndex.row()).get(qtModelIndex.column()); String column = elementForColumn.getColumn(); return column; default: 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_ROW, "row"); roles.put(ROLE_COLUMN, "column"); 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 コンテンツがロードされ、ステータスが準備完了になると、データモデルを設定します。
/* * 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(generateRow()); 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(); } }
本ドキュメントに含まれる文書の著作権は、それぞれの所有者に帰属します。 本書で提供されるドキュメントは、Free Software Foundation が発行したGNU Free Documentation License version 1.3に基づいてライセンスされています。 Qtおよびそれぞれのロゴは、フィンランドおよびその他の国におけるThe Qt Company Ltd.の 商標です。その他すべての商標は、それぞれの所有者に帰属します。