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.