Verwendung von QtAbstractItemModel in Android Studio Projekten

Überblick

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. In der Eigenschaft delegate definiert das Beispiel jedes Zellelement des Modells mit Text.

    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
        }

Im Delegaten setzt das Beispiel die Eigenschaften row und column über die Methoden QHash<int, QByteArray> QAbstractItemModel::roleNames() const und QtModelIndex index(int row, int column, QtModelIndex parent) und Object data(QtModelIndex qtModelIndex, int role) des Modells.

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

Siehe QAbstractItemModel für eine detaillierte Beschreibung.

    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
        }

Android Studio Projekt

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

Datenmodell

Das Datenmodell, c 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) {
    switch (role) {
        case ROLE_ROW:
            Cell elementForRow = m_dataList.get(qtModelIndex.row()).get(qtModelIndex.column());
            String row = String.valueOf(elementForRow.getRow());
            return row;
        case ROLE_COLUMN:
            Cell elementForColumn = m_dataList.get(qtModelIndex.row()).get(qtModelIndex.column());
            String column = elementForColumn.getColumn();
            return column;
        default:
            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_ROW, "row");
    roles.put(ROLE_COLUMN, "column");
    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 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(generateRow());
        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);
        generateColumn();
        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);
        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) {
    switch (role) {
        case ROLE_ROW:
            Cell elementForRow = m_dataList.get(qtModelIndex.row()).get(qtModelIndex.column());
            String row = String.valueOf(elementForRow.getRow());
            return row;
        case ROLE_COLUMN:
            Cell elementForColumn = m_dataList.get(qtModelIndex.row()).get(qtModelIndex.column());
            String column = elementForColumn.getColumn();
            return column;
        default:
            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_ROW, "row");
    roles.put(ROLE_COLUMN, "column");
    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.

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

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