Utilisation de QtAbstractItemModel dans les projets Android Studio
Qt Quick Les exemples de l'API Android sont des projets Android Studio
Le site Qt Quick pour les exemples de l'API Android est fourni en tant que projets Android Studio. Les dossiers des projets se trouvent dans votre emplacement d'installation de Qt Location.
Par exemple, sous le chemin d'installation par défaut de Windows, ils se trouvent ici :
C:\Qt\Examples\Qt-<patch-release-number>\platforms\android\<example-name>
Ces projets sont déjà configurés pour utiliser une version du plugin Qt Gradle compatible avec cette version de Qt.
Vue d'ensemble

Cet exemple se compose de deux projets : un projet Android Studio (qtabstractitemmodel_java) et un projet QML (qtabstractitemmodel). Vous pouvez importer le projet QML dans un projet Android.
L'exemple montre comment gérer des types de données complexes entre Java et QML. Il montre comment utiliser les classes API Java QtAbstractItemModel et QtModelIndex. En QML, l'utilisation des données est démontrée avec l'élément TableView. En Java, l'utilisation des données est démontrée avec un modèle d'éléments ArrayList imbriqués pour les lignes et les colonnes. Pour plus d'informations sur le fonctionnement de QML, voir Qt Qml.
Exécution de l'exemple
Pour exécuter cet exemple, vous avez besoin d'Android Studio et de Qt Tools for Android Studio en plus d'une installation standard de Qt pour Android. Ouvrez qtabstractitemmodel_java dans Android Studio et suivez les instructions de Qt Tools for Android Studio pour importer le projet qtabstractitemmodel.
Projet QML
Du côté du projet QML, l'exemple utilise un Rectangle comme objet racine. La variable de propriété dataModel contient le modèle de données créé et fourni par le côté Java.
Rectangle { id: mainRectangle property AbstractItemModel dataModel
TableView affiche notre modèle de données.
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 }
Sur la propriété delegate, chaque cellule du modèle est définie par un Rectangle contenant un TextEdit. La propriété texte du TextEdit est définie à l'aide de la méthode QAbstractItemModel::data() qui renvoie une valeur basée sur le rôle et l'index donnés.
L'appel de ces méthodes à partir de Qt Qml signifie que l'exécution a lieu dans le contexte du thread qtMainLoopThread de Qt.
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 } }
Dans le cas de la modification du champ TextEdit, le gestionnaire onEditingFinished() définit la valeur du rôle edit du modèle en fonction du texte modifié. Cela appelle la méthode QAbstractItemModel::setData(), où le texte édité de la cellule est mis à jour avec l'index correspondant du modèle.
Pour plus d'informations, voir QAbstractItemModel.
Projet Android Studio
Le projet Android Studio (qtabstractitemmodel_java) contient une classe Activity MainActivity et une classe MyDataModel.
Modèle de données
Le modèle de données, MyDataModel, étend la classe QtAbstractItemModel. La classe QtAbstractItemModel est une enveloppe pour la classe QAbstractItemModel.
Comme les méthodes de la classe MyDataModel sont appelées à la fois du côté Qt XML et du côté Android, l'exécution se produit dans les deux contextes de thread, Qt qtMainLoopThread, et les contextes de thread principal Android. Vous devez assurer la synchronisation lors de l'accès aux variables membres dans les méthodes de la classe MyDataModel.
Tout d'abord, l'exemple initialise le modèle avec un simple ensemble de données fictives de lignes et de colonnes. Notez que cette méthode de construction est appelée dans le contexte du fil d'exécution principal d'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() {L'exemple surcharge les méthodes de QtAbstractItemModel à des fins différentes. Les méthodes columnCount() et rowCount() renvoient le nombre de chaque élément d'un modèle. L'exécution de chaque rowCount() se produit dans les deux contextes de thread, Qt qtMainLoopThread et le contexte du thread principal d'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();
}La méthode data() fournit des données de modèle basées sur le rôle et l'index de Java à QML. La méthode roleNames() renvoie un hachage correspondant aux valeurs numériques des rôles et à leurs noms sous forme de chaînes de caractères ; en QML, nous utilisons ces noms de rôles pour extraire les données correspondantes du modèle. La méthode index() renvoie le nouvel index du modèle. La méthode parent() devrait renvoyer un parent de l'index. Cependant, comme cet exemple se concentre sur les données sans index parent, nous surchargeons la méthode et renvoyons un QtModelIndex() vide. Comme les méthodes sont appelées à partir de Qt Qml, l'exécution se produit dans le contexte du thread qtMainLoopThread de Qt.
/*
* 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();
}L'exemple surcharge la méthode QAbstractItemModel::setData(), qui est appelée lorsque les données du modèle à index sont définies du côté QML de l'application.
/*
* 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;
}L'exemple implémente des méthodes du côté du modèle pour l'interaction avec l'interface utilisateur MainActivity afin d'ajouter et de supprimer des lignes et des colonnes. Les appels de début, de fin, d'insertion et de suppression de lignes pour mettre à jour les index du modèle, comme beginInsertRow(). Comme l'exemple utilise le modèle QtAbstractItemModel, il doit appeler beginInsertRows() et endInsertRows() chaque fois qu'il insère de nouvelles lignes dans le modèle. Il en va de même pour la suppression. Comme les méthodes sont appelées du côté d'Android, l'exécution a lieu dans le contexte du thread principal d'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();
}
}L'exemple met en œuvre des méthodes du côté du modèle pour l'interaction de l'interface utilisateur MainActivity afin d'ajouter et de supprimer des colonnes. Les appels begin, end, insert et remove columns pour mettre à jour les index du modèle, comme beginRemoveColumn(). La sensibilisation au contexte est la même que pour les méthodes d'ajout et de suppression de lignes.
/*
* 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();
}
}Activité principale
MainActivity met en œuvre l'interface QtQmlStatusChangeListener pour obtenir des mises à jour d'état lorsque le QML est chargé. Il s'agit également de l'activité principale d'Android.
L'exemple crée et initialise le modèle de données. Voir aussi QtQuickView
private final MyDataModel m_model = new MyDataModel();
L'exemple définit le bouton de l'interface utilisateur et ses récepteurs pour permettre aux utilisateurs d'interagir avec le modèle via l'interface utilisateur.
/*
* 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();
}L'exemple commence à charger le contenu QML. Le chargement s'effectue en arrière-plan jusqu'à ce que l'état de ready soit mis à jour.
/*
* 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();
}L'exemple définit le modèle de données lorsque le contenu QML est chargé et que le statut est prêt.
/*
* 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.