QtAbstractListModel の QML への公開
概要
この例は、2つの別々のプロジェクトで構成されています:QML プロジェクトと、その QML コンテンツをホストし表示する Kotlin ベースの Android プロジェクトです。QtAbstractListModel を使って、Android 側から QML ビューにデータを共有し、ListView を使ってデータを表示する方法を示します。
例の実行
このサンプルを実行するには、Android Studio とQt for Androidが必要です。
Qt Gradle Pluginは、Androidプロジェクトのビルド過程でQMLプロジェクトのビルドに使用されます。そのため、この例ではアプリレベルのbuild.gradle.ktsファイルにプラグインの設定を記述していますが、プラグインがQtキットのディレクトリを見つけられない場合などは、プラグインの設定を変更する必要があります。
QtBuild { // Relative for Qt (Installer or MaintenanceTool) installations. qtPath = file("../../../../../../../6.8.2") projectPath = file("../../qtabstractlistmodel") }
プラグインの詳細な設定については、Qt Gradle Pluginのドキュメントを参照してください。
QMLプロジェクト
QMLプロジェクトは非常にシンプルで、ルートオブジェクトのプロパティとしてデータモデルを定義し、そのモデルからのデータを表示するためのいくつかのUI要素を定義します。
Rectangle { id: mainRectangle property AbstractItemModel dataModel
モデルからのデータを表示するために、ListView が作成されます。そして、model
プロパティが、先に宣言されたデータ・モデルに設定される。
ListView { id: listView model: mainRectangle.dataModel
データモデルを表示するには、ListView 、データモデルの各項目ごとにインスタンス化されるデリゲートが必要です。この場合、デリゲートはRectangle 、2つのText 要素をColumn に保持し、データ・モデルの各要素のデータを表示する。
delegate: Rectangle { required property var model width: listView.width height: textColumn.height + (2 * textColumn.spacing) color: "#2CDE85" radius: 25 Column { id: textColumn height: idText.height + rowText.height + spacing spacing: 15 anchors { verticalCenter: parent.verticalCenter left: parent.left right: parent.right leftMargin: 20 rightMargin: 20 } Text { id: idText color: "#00414A" text: model.id font.pixelSize: 36 font.bold: true } Text { id: rowText color: "#00414A" text: model.row font.pixelSize: 36 font.bold: true } } }
Kotlinプロジェクト
Android側は、1つのアクティビティと、先ほどQMLビューで使用したデータモデルの定義で構成されます。
データモデル
データモデルMyListModel
はQtAbstractListModel の子クラスで、ArrayList<String>
がデータの内部ストレージシステムです。MyListModel
のイニシャライザー・ブロックでは、リスト用のランダムなデータを生成します。
class MyListModel : QtAbstractListModel() { private val m_dataList = ArrayList<String>() init { synchronized(this) { for (row in 0..4) { m_dataList.add(UUID.randomUUID().toString()) } } }
モデルの各項目は、それに関連付けられたデータ要素のセットを持ち、それぞれが独自の役割を持っています。QtAbstractItemModel のカスタム実装では、各データ要素に対してカスタムのロールを定義する必要があります。各ロールは、データを取得する際に使用されるInt値と、QML から使用する際のデータ要素の名前を指定するString値を持ちます。
@Synchronized override fun roleNames(): HashMap<Int, String> { val m_roles = HashMap<Int, String>() m_roles[DataRole.UUID.value()] = "id" m_roles[DataRole.Row.value()] = "row" return m_roles }
"roleNames()"
メソッド内のInt
の値はハードコードされているかもしれませんが、この例ではMyListModel
の中にカスタム enum クラスDataRole
を指定し、これらの値を参照する際に使用しています。この例では、2つのロールを定義しています:UUIDとRowである。
private enum class DataRole(val m_value: Int) { UUID(0), Row(1); fun value(): Int { return m_value } companion object { fun valueOf(value: Int): DataRole? { val values = entries.toTypedArray() if (0 <= value && value < values.size) return values[value] return null } } }
データ・モデルからデータを返す場合、クラスは"QtAbstractListModel::data()"
メソッドをオーバーライドする必要があります。このメソッドは2つのパラメータを取ります:QtModelIndexとInt
は、それぞれデータ要素のインデックスとロールを表します。
"MyDataModel::data()"
では、UUID
の役割は内部データ内の指定されたインデックスからのデータを返し、Row
の役割は要求された要素の行を返します。
注: このメソッドは、他のいくつかのメソッドとともに、@Synchronizedタグでアノテーションされています。これは、これらのメソッドの呼び出しが Qt スレッドから発生し、"addRow()"
および"removeRow()"
メソッドを介した Android スレッドからのリクエストと同時に、基礎となるデータにアクセスする可能性があるためです。
@Synchronized override fun data(qtModelIndex: QtModelIndex, role: Int): Any { return when (DataRole.valueOf(role)) { DataRole.UUID -> "UUID: " + m_dataList[qtModelIndex.row()] DataRole.Row -> "Row: " + qtModelIndex.row() else -> "" } }
外部のアクターがQtAbstractItemModelを操作できるように、この例ではMyDataModel
に2つのメソッドを追加しています。行にデータを追加するには"addRow()"
、データを削除するには"removeRow()"
。これらはメイン・アクティビティから使用されます。
@Synchronized fun addRow() { beginInsertRows(QtModelIndex(), m_dataList.size, m_dataList.size) m_dataList.add(UUID.randomUUID().toString()) endInsertRows() } @Synchronized fun removeRow() { if (!m_dataList.isEmpty()) { beginRemoveRows(QtModelIndex(), m_dataList.size - 1, m_dataList.size - 1) m_dataList.removeAt(m_dataList.size - 1) endRemoveRows() } }
メイン・アクティビティ
MainActivity
クラスはシンプルなKotlinベースのアクティビティですが、QtQmlStatusChangeListenerというインターフェイスを実装しており、QMLの読み込みステータスイベントをリスニングします。また、QMLアプリケーションのメインビューのQtQuickViewContent
オブジェクトと、上記のデータモデルのインスタンスも格納しています。
class MainActivity : AppCompatActivity(), QtQmlStatusChangeListener { private val m_mainQmlContent: Main = Main() private val m_listModel = MyListModel()
アプリケーションのメインアクティビティを作成する際、このサンプルではまずQtQuickViewを作成し、ビュー階層に配置します。
val qtQuickView: QtQuickView = QtQuickView(this) val params: ViewGroup.LayoutParams = FrameLayout.LayoutParams( ViewGroup.LayoutParams.MATCH_PARENT, ViewGroup.LayoutParams.MATCH_PARENT ) val qmlFrameLayout: FrameLayout = findViewById<FrameLayout>(R.id.qmlFrame) qmlFrameLayout.addView(qtQuickView, params)
QtQuickViewをUIに追加した後、データモデルを操作するためのボタンを見つけ、メンバーデータモデルのaddRow()
、removeRow()
を呼び出すためのクリックリスナーを設定します。
val addRowAtEndButton: Button = findViewById<Button>(R.id.addRow) val removeRowFromEndButton: Button = findViewById<Button>(R.id.removeRow) addRowAtEndButton.setOnClickListener { _: View? -> m_listModel.addRow() } removeRowFromEndButton.setOnClickListener { _: View? -> m_listModel.removeRow() }
UIのセットアップとリスナーが完了したら、QMLコンポーネントの準備と読み込みを行います。この例では、MainActivity
をQMLコンポーネントの状態変化シグナルのリスナーとして設定し、QtQuickView
にQMLコンポーネントを読み込むように指示しています。
m_mainQmlContent.setStatusChangeListener(this) qtQuickView.loadContent(m_mainQmlContent)
最後に、QML コンポーネントの読み込みに成功したら、MyDataModel インスタンスの値を QML コンポーネントのdataModel
プロパティに代入します。
override fun onStatusChanged(qtQmlStatus: QtQmlStatus) { if (qtQmlStatus === QtQmlStatus.READY) { m_mainQmlContent.setDataModel(m_listModel) } }
© 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.