在 Android Studio 项目中使用 QtAbstractItemModel
Qt Quick Android API 示例均为 Android Studio 项目
Qt Quick 的 Android API 示例以 Android Studio 项目形式提供。这些项目文件夹位于您的 Qt 安装目录中。
例如,在 Windows 的默认安装路径下,它们位于此处:
C:\Qt\Examples\Qt-<patch-release-number>\platforms\android\<example-name>这些项目已预先配置为使用与该 Qt 版本兼容的Qt Gradle 插件。
概述

本示例由两个项目组成:一个 Android Studio 项目(qtabstractitemmodel_java)和一个 QML 项目(qtabstractitemmodel)。您可以将 QML 项目导入到 Android 项目中。
该示例展示了如何在 Java 和 QML 之间处理复杂数据类型,并演示了如何使用QtAbstractItemModel和QtModelIndex这两种 Java API 类。 在 QML 中,通过 `TableView ` 项演示了数据的使用方式。在 Java 中,则通过一个由嵌套的 `ArrayList` 项组成的模型(用于行和列)演示了数据的使用方式。有关 QML 工作原理的更多信息,请参阅 Qt Qml。
运行示例
要运行此示例,您需要在标准 Qt for Android 安装基础上,额外安装 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 dataModelTableView 显示我们的数据模型。
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 属性中,模型的每个单元格项都通过包含TextEdit 的Rectangle 进行定义。TextEdit 的text属性通过QAbstractItemModel::data()方法设置,该方法会根据给定的角色和索引返回相应值。
从 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 类的方法既会在 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() 方法返回一个哈希表,将数值角色与字符串形式的名称进行映射;在 QML 中,我们使用这些角色名称从模型中获取相应数据。 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()方法,当应用程序的QML侧设置index 中的模型数据时,该方法会被调用。
/*
* 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交互的方法,以添加和删除行与列。 调用 begin、end、insert 和 remove 行来更新模型索引,例如 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 用户界面交互以添加和删除列的方法。调用开始、结束、插入和删除列的方法来更新模型索引,例如 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 实现了QtQmlStatusChangeListener接口,以便在加载 QML 时获取状态更新。它同时也是 Android 端的主 Activity。
该示例创建并初始化了数据模型。另请参阅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;
}© 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.