Verwendung von QtAbstractItemModel in Android Studio-Projekten

Die Qt Quick for Android API Beispiele werden als Android Studio Projekte bereitgestellt. Die Projektordner befinden sich in Ihrem Qt-Installationsverzeichnis.

Unter dem Standard-Windows-Installationspfad finden Sie sie zum Beispiel hier:

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

Übersicht

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 man komplexe Datentypen zwischen Java und QML handhabt. Es zeigt, wie die Java-API-Klassen QtAbstractItemModel und QtModelIndex verwendet werden können. In QML wird die Datenverwendung mit dem Element TableView demonstriert. In Java wird die Datennutzung anhand eines Modells mit verschachtelten ArrayList-Elementen für Zeilen und Spalten demonstriert. Weitere Informationen darüber, wie QML funktioniert, finden Sie unter Qt Qml.

Ausführen des Beispiels

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

QML-Projekt

Auf der Seite des QML-Projekts verwendet das Beispiel ein Rectangle als Stammobjekt. Die Eigenschaftsvariable dataModel enthält das von der Java-Seite erstellte und gelieferte Datenmodell.

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
        }

In der Eigenschaft delegate wird jedes Zellelement des Modells mit einem Rectangle definiert, das ein TextEdit enthält. Die Texteigenschaft von TextEdit wird mit QAbstractItemModel::data() festgelegt, das einen Wert auf der Grundlage der angegebenen Rolle und des Index zurückgibt.

Der Aufruf dieser Methoden aus QML bedeutet, dass die Ausführung im Qt qtMainLoopThread Thread-Kontext stattfindet.

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

Im Falle der Bearbeitung des Feldes TextEdit setzt der onEditingFinished() Handler den Wert der Rolle edit des Modells auf den bearbeiteten Text. Dies ruft die Methode QAbstractItemModel::setData() auf, wo der bearbeitete Text der Zelle auf den entsprechenden Index des Modells aktualisiert wird.

Für weitere Informationen siehe 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. Die Klasse 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-Kontext. Sie müssen die Synchronisation beim Zugriff auf Membervariablen in Methoden der Klasse MyDataModel sicherstellen.

Zunächst initialisiert das Beispiel das Modell mit einem einfachen Zeilen- und Spalten-Mock-Datensatz. Beachten Sie, dass diese Konstruktormethode im Android-Hauptthread-Kontext 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() {

Im Beispiel werden die Methoden von QtAbstractItemModel für verschiedene Zwecke überschrieben. Die Methoden columnCount() und rowCount() geben die Anzahl der einzelnen Spalten im Modell zurück. Die Ausführung von rowCount() erfolgt in beiden Thread-Kontexten, Qt qtMainLoopThread und Android Main Thread-Kontext.

/*
* 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() liefert Modelldaten basierend auf der Rolle und dem Index von Java zu QML. Die Methode roleNames() gibt einen Hash zurück, der numerische Rollenwerte mit ihren Namen als Strings vergleicht; in QML verwenden wir diese Rollennamen, um die entsprechenden Daten aus dem Modell zu holen. Die Methode index() gibt den neuen Modellindex zurück. Die Methode parent() sollte einen Elternteil 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 von QML aus aufgerufen werden, erfolgt die Ausführung im Qt qtMainLoopThread Thread-Kontext.

/*
* 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 Daten des Modells auf index von der QML-Seite der Anwendung 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 Methoden auf der Modellseite für MainActivity UI-Interaktion zum Hinzufügen und Entfernen von Zeilen und Spalten. Ruft begin, end, insert und remove rows auf, um Modellindizes zu aktualisieren, wie beginInsertRow(). Da das Beispiel die QtAbstractItemModel verwendet, muss es beginInsertRows() und endInsertRows() jedes Mal aufrufen, wenn es neue Zeilen in das Modell einfügt. Dasselbe gilt für das Entfernen. Da die Methoden von der Android-Seite aus aufgerufen werden, findet die Ausführung im Kontext des Android-Hauptthreads statt.

/*
* 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 Methoden auf der Modellseite für MainActivity UI-Interaktion zum Hinzufügen und Entfernen von Spalten. Die Aufrufe begin, end, insert und remove columns aktualisieren Modellindizes, wie beginRemoveColumn(). Es gilt das gleiche Kontextbewusstsein wie bei den Methoden add und remove row.

/*
* 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 die QML geladen wird. Sie ist auch die wichtigste Android-Aktivität.

Das Beispiel erstellt und initialisiert das Datenmodell. Siehe auch QtQuickView

private final MyDataModel m_model = new MyDataModel();

Das Beispiel setzt die UI-Schaltfläche und ihre Listener, 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 von 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, wenn der QML-Inhalt geladen ist und der Status bereit 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;
}

© 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.