このページでは

QtAbstractListModelをQMLから利用可能にする

Qt Quick Android API のサンプルは Android Studio プロジェクトとして提供されています

Android向けQt Quick APIのサンプルは、Android Studioプロジェクトとして提供されています。プロジェクトフォルダは、Qtのインストール先にあります。

たとえば、Windows のデフォルトのインストールパスでは、以下の場所にあります:

C:\Qt\Examples\Qt-<patch-release-number>\platforms\android\<example-name>

これらのプロジェクトは、この Qt バージョンと互換性のあるQt Gradle プラグインのバージョンを使用するよう、すでに設定されています。

概要

UUIDと行データを表示するListViewを備え、「行の追加」および「行の削除」ボタンが搭載されたAndroidアプリ

このサンプルは、2つの別々のプロジェクトで構成されています。1つはQMLプロジェクト、もう1つはQMLコンテンツをホストして表示するKotlinベースのAndroidプロジェクトです。このサンプルでは、QtAbstractListModelを使用して、Android側からListView を使用してデータを表示するQMLビューへデータを共有する方法を示しています。

例の動作確認

このサンプルを実行するには、Android Studio およびQt for Androidのインストールが必要です。

Androidプロジェクトのビルドプロセス中に、Qt Gradleプラグインを使用してQmlプロジェクトをビルドします。このため、このサンプルではアプリレベルのbuild.gradle.ktsファイルにプラグインの設定が記述されていますが、例えばプラグインがQtキットディレクトリを見つけられない場合などには、この設定を変更する必要があるかもしれません。

QtBuild {
    // Relative for Qt (Installer or MaintenanceTool) installations.
    qtPath = file("../../../../../../../6.12.0")
    projectPath = file("../../qtabstractlistmodel")
}

プラグインの詳細な設定については、Qt Gradle プラグインのドキュメントを参照してください。

QML プロジェクト

QMLプロジェクトは非常に単純で、ルートオブジェクトのプロパティとしてデータモデルを定義し、そのモデルからのデータを表示するためのUI要素をいくつか定義しています。

Rectangle {
    id: mainRectangle

    property AbstractItemModel dataModel

モデルからのデータを表示するために、ListView が作成されます。その後、model プロパティに、先に宣言したデータモデルが設定されます。

    ListView {
        id: listView

        model: mainRectangle.dataModel

データモデルを表示するために、ListView にはデリゲートが必要であり、これはデータモデル内の各項目に対してインスタンス化されます。この場合、デリゲートはRectangle となり、Column 内に2つのText 要素を保持し、データモデル内の各要素のデータを表示します。

        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つのActivityと、前述の 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 内でカスタム列挙型クラスDataRole を指定しており、これらの値を参照する際にこれを使用しています。この例では、UUID と Row の 2 つのロールを定義しています。

    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()" メソッドをオーバーライドする必要があります。このメソッドは、データ要素のインデックスとロールをそれぞれ指すQtModelIndexと Int の2つのパラメータを受け取ります。

"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ベースの単純なActivityですが、QMLの読み込みステータスイベントを監視するためにQtQmlStatusChangeListenerインターフェースも実装しています。また、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)
        }
    }

© 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.