Sur cette page

Utilisation de QtAbstractItemModel dans les projets Android Studio

Qt Quick Les exemples d'API Android sont des projets Android Studio

Les exemples d'API « Qt Quick » pour Android sont fournis sous forme de projets Android Studio. Les dossiers de projet se trouvent dans le répertoire d'installation de Qt.

Par exemple, sous le chemin d'installation par défaut sous 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 Gradle Qt compatible avec cette version de Qt.

Présentation

Application Android avec TableView affichant une grille de cellules modifiables et des boutons de contrôle des lignes et des colonnes

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.

Cet exemple montre comment gérer des types de données complexes entre Java et QML. Il illustre l’utilisation des classes d’API Java QtAbstractItemModel et QtModelIndex. En QML, l’utilisation des données est illustrée à l’aide de l’élément ` TableView `. En Java, l’utilisation des données est illustrée à l’aide d’un modèle d’éléments `ArrayList` imbriqués pour les lignes et les colonnes. Pour plus d’informations sur le fonctionnement de QML, consultez Qt Qml.

Exécution de l'exemple

Pour exécuter cet exemple, vous devez disposer d’Android Studio et de Qt Tools for Android Studio, en plus d’une installation standard de Qt pour Android. Ouvrez le fichier qtabstractitemmodel_java dans Android Studio et suivez les instructions fournies dans Qt Tools for Android Studio pour importer l’ 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
        }

Au niveau de la propriété ` delegate `, chaque élément de cellule du modèle est défini par un ` Rectangle ` contenant un ` TextEdit`. La propriété `text` de l’` TextEdit ` est définie à l’aide de la méthode ` QAbstractItemModel::data()`, qui renvoie une valeur en fonction du rôle et de l’index fournis.

L'appel de ces méthodes depuis Qml signifie que l'exécution s'effectue 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
            }
        }

Lors de la modification du champ « TextEdit », le gestionnaire ` onEditingFinished() ` attribue au rôle « edit » du modèle la valeur du texte modifié. Cela appelle la méthode ` QAbstractItemModel::setData()`, qui met à jour le texte modifié de la cellule à l’index correspondant du modèle.

Pour plus d’informations, consultez 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 un wrapper pour QAbstractItemModel.

Comme les méthodes de la classe MyDataModel sont appelées à la fois depuis le côté QML et depuis le côté Android, leur exécution s'effectue dans les deux contextes de thread : le thread qtMainLoopThread de Qt et le thread principal d'Android. Vous devez assurer la synchronisation lorsque vous accédez aux variables membres dans les méthodes de la classe MyDataModel.

Tout d’abord, l’exemple initialise le modèle avec un ensemble de données factices simple composé de lignes et de colonnes. Notez que cette méthode de constructeur est appelée dans le contexte du thread principal 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 redéfinit les méthodes de `QtAbstractItemModel` à des fins diverses. Les méthodes `columnCount()` et `rowCount()` renvoient respectivement le nombre de colonnes et de lignes dans un modèle. L’exécution de chaque appel à `rowCount()` se produit dans les deux contextes de thread : le thread `qtMainLoopThread` de Qt et le 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 les données du modèle à partir de Java vers QML en fonction du rôle et de l’index. La méthode roleNames() renvoie un hachage associant les valeurs numériques des rôles à leurs noms sous forme de chaînes de caractères ; en QML, nous utilisons ces noms de rôles pour récupérer les données correspondantes dans le 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 redéfinissons la méthode et renvoyons un `QtModelIndex()` vide. Comme les méthodes sont appelées depuis QML, l’exécution s’effectue dans le contexte de thread 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();
}

L’exemple redéfinit la méthode ` QAbstractItemModel::setData()`, qui est appelée lorsque les données du modèle à l’adresse ` index ` sont définies depuis le 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 côté modèle pour permettre l’interaction avec l’interface utilisateur d’ MainActivity afin d’ajouter et de supprimer des lignes et des colonnes. Il appelle les méthodes begin, end, insert et remove pour mettre à jour les index du modèle, comme beginInsertRow(). Comme l’exemple utilise 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 depuis le côté Android, l’exécution s’effectue 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 implémente des méthodes côté modèle pour l’interaction de l’interface utilisateur d’ MainActivity, permettant d’ajouter et de supprimer des colonnes. Il appelle les méthodes begin, end, insert et remove pour les colonnes afin de mettre à jour les index du modèle, comme beginRemoveColumn(). La même prise en compte du contexte s’applique qu’avec 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();
    }
}

L'activité principale

MainActivity implémente l’interface QtQmlStatusChangeListener pour recevoir les mises à jour d’état lors du chargement du QML. Il s’agit également de l’activité Android principale.

L'exemple crée et initialise le modèle de données. Voir également QtQuickView

private final MyDataModel m_model = new MyDataModel();

L'exemple configure le bouton de l'interface utilisateur et ses écouteurs 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 lance le chargement du contenu QML. Le chargement s'effectue en arrière-plan jusqu'à ce que l'état « 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 configure le modèle de données une fois le contenu QML chargé et l'état « ready » atteint.

/*
* 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.