Auf dieser Seite

Verwendung von QtAbstractItemModel in Android Studio-Projekten

Qt Quick Die Beispiele für die Android-API sind Android-Studio-Projekte

Die Beispiele für die „ Qt Quick “-API für Android werden als Android Studio-Projekte bereitgestellt. Die Projektordner befinden sich in Ihrem Qt-Installationsverzeichnis.

Beispielsweise befinden sie sich unter dem Standard-Installationspfad unter Windows hier:

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

Diese Projekte sind bereits für die Verwendung einer mit dieser Qt-Version kompatiblen Version des Qt-Gradle-Plugins konfiguriert.

Übersicht

Android-App mit TableView, die ein bearbeitbares Zellraster sowie Steuerelemente für Zeilen und Spalten anzeigt

Dieses Beispiel besteht aus zwei Projekten: einem Android Studio-Projekt (qtabstractitemmodel_java) und einem QML-Projekt (qtabstractitemmodel). Sie können das QML-Projekt in ein Android-Projekt importieren.

Das Beispiel zeigt, wie komplexe Datentypen zwischen Java und QML gehandhabt werden. Es veranschaulicht die Verwendung der Java-API-Klassen „QtAbstractItemModel“ und „QtModelIndex “. In QML wird die Datennutzung anhand des Elements „ TableView “ veranschaulicht. In Java wird die Datennutzung anhand eines Modells aus verschachtelten ArrayList-Elementen für Zeilen und Spalten veranschaulicht. Weitere Informationen zur Funktionsweise von QML finden Sie unter Qt Qml.

Ausführen des Beispiels

Um dieses Beispiel auszuführen, benötigen Sie Android Studio und die Qt-Tools für Android Studio zusätzlich zu einer Standardinstallation von Qt für Android. Öffnen Sie „qtabstractitemmodel_java“ in Android Studio und befolgen Sie die Anweisungen in den Qt-Tools für Android Studio, um das „ qtabstractitemmodel “-Element zu importieren.

QML-Projekt

Auf der Seite des QML-Projekts verwendet das Beispiel ein „ Rectangle “ als Stammobjekt. Die Eigenschaftsvariable „ dataModel “ enthält das Datenmodell, das auf der Java-Seite erstellt und bereitgestellt wurde.

Rectangle {
    id: mainRectangle

    property AbstractItemModel dataModel

TableView zeigt unser Datenmodell an.

    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
        }

Bei der Eigenschaft „ delegate “ wird jedes Zellenelement des Modells mit einem „ Rectangle “ definiert, das ein „ TextEdit “ enthält. Die Eigenschaft „text“ des „ TextEdit “ wird mithilfe der Methode „ QAbstractItemModel::data()“ gesetzt, die einen Wert basierend auf der angegebenen Rolle und dem Index zurückgibt.

Der Aufruf dieser Methoden aus Qml bedeutet, dass die Ausführung im Thread-Kontext von Qt `qtMainLoopThread` erfolgt.

        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
            }
        }

Beim Bearbeiten des Feldes „ TextEdit “ setzt der Handler „ onEditingFinished() “ den Wert der Rolle „ edit “ des Modells auf den bearbeiteten Text. Dadurch wird die Methode „ QAbstractItemModel::setData()“ aufgerufen, wodurch der bearbeitete Text der Zelle am entsprechenden Index des Modells aktualisiert wird.

Weitere Informationen finden Sie unter QAbstractItemModel.

Android Studio-Projekt

Das Android Studio-Projekt (qtabstractitemmodel_java) enthält eine Activity-Klasse „ MainActivity “ und eine „ MyDataModel “-Klasse.

Datenmodell

Das Datenmodell „ MyDataModel “ erweitert die Klasse „QtAbstractItemModel “. „ QtAbstractItemModel “ ist ein Wrapper für „ QAbstractItemModel “.

Da die Methoden der Klasse „ MyDataModel “ sowohl von der QML- als auch von der Android-Seite aufgerufen werden, erfolgt die Ausführung in beiden Thread-Kontexten: dem Qt-qtMainLoopThread und dem Android-Hauptthread. Sie müssen die Synchronisation sicherstellen, wenn Sie in Methoden der Klasse „ MyDataModel “ auf Membervariablen zugreifen.

Zunächst initialisiert das Beispiel das Modell mit einem einfachen Mock-Datensatz aus Zeilen und Spalten. Beachten Sie, dass diese Konstruktormethode im Haupt-Thread-Kontext von Android aufgerufen wird.

/*
* 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() {

Das Beispiel überschreibt die Methoden von `QtAbstractItemModel` für verschiedene Zwecke. Die Methoden `columnCount()` und `rowCount()` geben die jeweilige Anzahl im Modell zurück. Die Ausführung von `rowCount()` erfolgt in beiden Thread-Kontexten: dem Qt-`qtMainLoopThread` und dem Android-Hauptthread.

/*
* 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();
}

Die Methode `data()` stellt Modelldaten basierend auf der Rolle und dem Index von Java an QML bereit. Die Methode `roleNames()` gibt ein Hash zurück, das numerische Rollenwerte ihren Namen als Zeichenketten zuordnet; in QML verwenden wir diese Rollennamen, um entsprechende Daten aus dem Modell abzurufen. Die Methode `index()` gibt den neuen Modellindex zurück. Die Methode `parent()` sollte einen übergeordneten Eintrag des Indexes zurückgeben. Da sich dieses Beispiel jedoch auf Daten ohne übergeordnete Indizes konzentriert, überschreiben wir die Methode und geben einen leeren `QtModelIndex()` zurück. Da die Methoden aus QML aufgerufen werden, erfolgt die Ausführung im Thread-Kontext „qtMainLoopThread“ von 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();
}

Das Beispiel überschreibt die Methode ` QAbstractItemModel::setData()`, die aufgerufen wird, wenn die Modelldaten unter ` index ` von der QML-Seite der Anwendung aus gesetzt werden.

/*
* 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;
}

Das Beispiel implementiert auf der Modellseite Methoden für die UI-Interaktion mit „ MainActivity “, um Zeilen und Spalten hinzuzufügen und zu entfernen. Es werden Methoden zum Starten, Beenden, Einfügen und Entfernen von Zeilen aufgerufen, um die Modellindizes zu aktualisieren, wie z. B. `beginInsertRow()`. Da das Beispiel das `QtAbstractItemModel` verwendet, muss es jedes Mal, wenn neue Zeilen in das Modell eingefügt werden, `beginInsertRows()` und `endInsertRows()` aufrufen. Das Gleiche gilt für das Entfernen. Da die Methoden von der Android-Seite aus aufgerufen werden, erfolgt die Ausführung im Kontext des Android-Hauptthreads.

/*
* 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();
    }
}

Das Beispiel implementiert modellseitige Methoden für die UI-Interaktion mit „ MainActivity “, um Spalten hinzuzufügen und zu entfernen. Es ruft Methoden zum Starten, Beenden, Einfügen und Entfernen von Spalten auf, um die Modellindizes zu aktualisieren, wie z. B. `beginRemoveColumn()`. Es gilt dieselbe Kontextabhängigkeit wie bei den Methoden zum Hinzufügen und Entfernen von Zeilen.

/*
* 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();
    }
}

Hauptaktivität

MainActivity implementiert die Schnittstelle `QtQmlStatusChangeListener `, um Statusaktualisierungen zu erhalten, wenn das QML geladen wird. Sie ist gleichzeitig die Hauptaktivität von Android.

Das Beispiel erstellt und initialisiert das Datenmodell. Siehe auch QtQuickView

private final MyDataModel m_model = new MyDataModel();

Das Beispiel richtet die UI-Schaltfläche und ihre Listener ein, damit die Benutzer über die Benutzeroberfläche mit dem Modell interagieren können.

/*
* 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();
}

Das Beispiel beginnt mit dem Laden des QML-Inhalts. Das Laden erfolgt im Hintergrund, bis der Status „ ready “ aktualisiert wird.

/*
* 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();
}

Das Beispiel legt das Datenmodell fest, sobald der QML-Inhalt geladen ist und der Status „ready“ erreicht ist.

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