将 QtAbstractListModel 暴露于 QML

Qt Quick for Android API 示例以 Android Studio 项目的形式提供。项目文件夹位于 Qt 安装位置。

例如,在默认的 Windows 安装路径下,可在此处找到:

C:\Qt\Examples\Qt-/1\platforms\android

概述

本示例由两个独立的项目组成:一个 QML 项目和一个基于 Kotlin 的 Android 项目,后者将托管并显示 QML 内容。它展示了如何使用QtAbstractListModel将数据从 Android 端共享到 QML 视图,该视图使用ListView 显示数据。

运行示例

要运行此示例,您需要安装 Android Studio 和Qt for Android

在 Android 项目构建过程中,将使用Qt Gradle Plugin来构建 QML 项目。为此,本示例在应用级 build.gradle.kts 文件中进行了一些插件配置,如果插件找不到 Qt kit 目录等,可能需要修改这些配置。

QtBuild {
    // Relative for Qt (Installer or MaintenanceTool) installations.
    qtPath = file("../../../../../../../6.9.1")
    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 ,在Column 中保存两个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 端由单个Activity和之前在 QML 视图中使用的数据模型定义组成。

数据模型

数据模型MyListModelQtAbstractListModel 的子类,其中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值(在检索数据时使用)和一个String值(在从 QML 使用时指定数据元素的名称)。

    @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 和行。

    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()" 方法。该方法需要两个参数:QtModelIndexInt ,它们分别指数据元素的索引和角色。

"MyDataModel::data()" 中,UUID 角色返回内部数据中给定索引的数据,而Row 角色返回请求元素的行。

注: 此方法和其他一些方法都注有@Synchronized标记。这是因为对这些方法的调用来自 Qt XML 线程,并可能与 Android 线程通过"addRow()""removeRow()" 方法发出的请求同时访问底层数据。

    @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 中添加了两个方法。要向行中添加数据,有"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,但也实现了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 添加到用户界面后,示例会找到用于操作数据模型的按钮,并设置一些点击监听器,以调用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()
        }

完成用户界面设置和监听器后,就可以准备和加载 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.