Android: Add documentation for QtAbstractItemModel Java API example
Adds documentation for Android Studio example application project. Explains how to handle complex data types between Java and QML. Explains the project structure and thread contextes with code examples. Example opens up the usage of two Java API classes QtAbstractItemModel (QtAIM) and QtModelIndex (QtMI). Task-number: QTBUG-126457 Pick-to: 6.8 Change-Id: Ibbd10b74aa415985f3edd0affa8d0301d71aa8c0 Reviewed-by: Nicholas Bennett <nicholas.bennett@qt.io> Reviewed-by: Rami Potinkara <rami.potinkara@qt.io>
This commit is contained in:
parent
036beb9962
commit
ce65c63748
Binary file not shown.
After Width: | Height: | Size: 36 KiB |
|
@ -0,0 +1,138 @@
|
|||
// Copyright (C) 2024 The Qt Company Ltd.
|
||||
// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GFDL-1.3-no-invariants-only
|
||||
|
||||
/*!
|
||||
\page qtabstractitemmodel-in-android-studio-projects-example.html
|
||||
\title QtAbstractItemModel Example in Android Studio Projects
|
||||
\brief Shows how to handle complex data types and apply conditional
|
||||
formatting to a table view format using QtAbstractItemModel class and
|
||||
QtModelIndex class.
|
||||
\ingroup qtquickexamples
|
||||
|
||||
\section1 Overview
|
||||
|
||||
\image qtabstractitemmodel_portrait.png
|
||||
|
||||
This example consists of two projects: an Android Studio project
|
||||
(qtabstractitemmodel_java) and a QML project (qtabstractitemmodel).
|
||||
You can import the QML project into an Android project.
|
||||
|
||||
The example shows how to handle complex data types between Java and QML.
|
||||
It demonstrates how to use the \c QtAbstractItemModel and \c QtModelIndex
|
||||
Java API classes. In QML, the data usage is demonstrated with the TableView item.
|
||||
In Java, data usage is demonstrated with a model of nested ArrayList items
|
||||
for rows and columns. For more information on how QML works, see \l {Qt Qml}.
|
||||
|
||||
\section1 Running the example
|
||||
|
||||
To run this example, you need Android Studio and
|
||||
\l {Qt Tools for Android Studio} on top of a standard Qt for Android installation.
|
||||
Open qtabstractitemmodel_java in Android Studio and follow the instructions
|
||||
in \l {Qt Tools for Android Studio} to import the \c qtabstractitemmodel.
|
||||
|
||||
\section1 QML project
|
||||
|
||||
On the QML project side, the example uses a \l {Rectangle QML Type} as the root
|
||||
object. The \c dataModel property variable holds the data model created and
|
||||
delivered from the Java side.
|
||||
|
||||
\snippet android/models/qtabstractitemmodel/Main.qml 0
|
||||
|
||||
\l {TableView QML Type} displays our data model. On the \c delegate property, the example
|
||||
defines each cell item of the model with \l {Text QML Type}.
|
||||
|
||||
\snippet android/models/qtabstractitemmodel/Main.qml 1
|
||||
|
||||
In the delegate, the example sets the \c row and \c column properties through the
|
||||
\l {QAbstractItemModel Class}{QHash<int, QByteArray> QAbstractItemModel::roleNames() const}
|
||||
and \l {QAbstractItemModel Class}{ QtModelIndex index(int row, int column, QtModelIndex parent) }
|
||||
and \l {QAbstractItemModel Class}{Object data(QtModelIndex qtModelIndex, int role) }
|
||||
methods of the model.
|
||||
|
||||
Calling these methods from QML means the execution takes place in the Qt qtMainLoopThread
|
||||
thread context.
|
||||
|
||||
See \l {QAbstractItemModel Class}{QAbstractItemModel} for a detailed description.
|
||||
|
||||
\snippet android/models/qtabstractitemmodel/Main.qml 1
|
||||
|
||||
\section1 Android Studio project
|
||||
|
||||
The Android Studio project (qtabstractitemmodel_java) contains
|
||||
one Activity class \c MainActivity and \c MyDataModel class.
|
||||
|
||||
\section2 Data Model
|
||||
|
||||
The data model, c\ MyDataModel, extends \c QtAbstractItemModel class.
|
||||
The \c QtAbstractItemModel is a wrapper for \l {QAbstractItemModel Class}.
|
||||
|
||||
As the methods of \c MyDataModel class are called from both, QML and Android sides, the
|
||||
execution occurs in both thread contexts, Qt qtMainLoopThread, and Android main thread
|
||||
contexts. You must ensure synchronization when accessing member variables in methods of
|
||||
the \c MyDataModel class.
|
||||
|
||||
First, the example initializes the model with a simple row and column mock data set. Note
|
||||
that this constructor method is called in the Android main thread context.
|
||||
|
||||
\snippet android/models/qtabstractitemmodel_java/app/src/main/java/com/example/qtabstractitemmodel_java/MyDataModel.java 1
|
||||
|
||||
The example overrides the \c QtAbstractItemModel methods for different purposes. The
|
||||
columnCount() and rowCount() methods return the count of each in a model. The execution of
|
||||
each rowCount() occurs in both thread contexts, Qt qtMainLoopThread and Android main thread
|
||||
contexts.
|
||||
|
||||
\snippet android/models/qtabstractitemmodel_java/app/src/main/java/com/example/qtabstractitemmodel_java/MyDataModel.java 2
|
||||
|
||||
Method data() provides model data based on the role and index from Java to QML. The
|
||||
roleNames() method returns a hash matching numerical role values to their names as strings;
|
||||
in QML, we use these role names to fetch corresponding data from the model. The index()
|
||||
method returns the new model index. Method parent() should return a parent of the index.
|
||||
Still, as this example focuses on data without parent indices, we override the method and
|
||||
return an empty QtModelIndex(). As the methods are called from QML, the execution occurs in
|
||||
the Qt qtMainLoopThread thread context.
|
||||
|
||||
|
||||
\snippet android/models/qtabstractitemmodel_java/app/src/main/java/com/example/qtabstractitemmodel_java/MyDataModel.java 3
|
||||
|
||||
The example implements methods on the model side for \c MainActivity UI interaction to add
|
||||
and remove rows and columns. Calls begin, end, insert, and remove rows to update model
|
||||
indexes, like beginInsertRow().
|
||||
Because the example uses the \c QtAbstractItemModel, it must call beginInsertRows() and
|
||||
endInsertRows() every time it inserts new rows into the model. The same applies to removal.
|
||||
As the methods are called from the Android side, the execution takes place in the Android
|
||||
main thread context.
|
||||
|
||||
\snippet android/models/qtabstractitemmodel_java/app/src/main/java/com/example/qtabstractitemmodel_java/MyDataModel.java 4
|
||||
|
||||
The example implements methods on the model side for \c MainActivity UI interaction to add
|
||||
and remove columns. Calls begin, end, insert, and remove columns to update model indexes,
|
||||
like beginRemoveColumn(). The same context awareness applies as with the add and remove row
|
||||
methods.
|
||||
|
||||
\snippet android/models/qtabstractitemmodel_java/app/src/main/java/com/example/qtabstractitemmodel_java/MyDataModel.java 5
|
||||
|
||||
\section2 Main Activity
|
||||
|
||||
\c MainActivity implements the \c QtQmlStatusChangeListener interface to get status updates
|
||||
when the QML is loaded. It is also the main Android activity.
|
||||
|
||||
The example creates and initializes the data model. See also
|
||||
\l {Qt Quick View Android Class}{QtQuickView}
|
||||
|
||||
\snippet android/models/qtabstractitemmodel_java/app/src/main/java/com/example/qtabstractitemmodel_java/MainActivity.java 1
|
||||
|
||||
The example sets the UI button and its listeners to allow the users to interact with the
|
||||
model via the UI.
|
||||
|
||||
\snippet android/models/qtabstractitemmodel_java/app/src/main/java/com/example/qtabstractitemmodel_java/MyDataModel.java 2
|
||||
|
||||
The example starts loading the QML content. Loading happens in the background until the
|
||||
\c ready status is updated.
|
||||
|
||||
\snippet android/models/qtabstractitemmodel_java/app/src/main/java/com/example/qtabstractitemmodel_java/MyDataModel.java 3
|
||||
|
||||
The example sets the data model when the QML content is loaded, and the status is ready.
|
||||
|
||||
\snippet android/models/qtabstractitemmodel_java/app/src/main/java/com/example/qtabstractitemmodel_java/MyDataModel.java 4
|
||||
|
||||
*/
|
|
@ -3,15 +3,17 @@
|
|||
import QtQuick
|
||||
import QtQuick.Controls
|
||||
|
||||
//! [0]
|
||||
Rectangle {
|
||||
id: mainRectangle
|
||||
|
||||
property AbstractItemModel dataModel
|
||||
|
||||
//! [0]
|
||||
color: "#00414A"
|
||||
border.color: "#00414A"
|
||||
border.width: 2
|
||||
|
||||
//! [1]
|
||||
TableView {
|
||||
id: tableView
|
||||
|
||||
|
@ -29,7 +31,9 @@ Rectangle {
|
|||
ScrollBar.horizontal: ScrollBar{
|
||||
policy: ScrollBar.AsNeeded
|
||||
}
|
||||
//! [1]
|
||||
|
||||
//! [2]
|
||||
delegate: Rectangle {
|
||||
width: tableView.width
|
||||
color: "#2CDE85"
|
||||
|
@ -37,11 +41,12 @@ Rectangle {
|
|||
border.width: 2
|
||||
Text {
|
||||
// Calls MyDataModel::data to get data based on the roles.
|
||||
// Calls in a context of a QtRenderingThread TODO check and synch
|
||||
// Called in Qt qtMainLoopThread thread context.
|
||||
text: model.row + model.column
|
||||
font.pixelSize: 26
|
||||
font.bold: true
|
||||
}
|
||||
}
|
||||
//! [2]
|
||||
}
|
||||
}
|
||||
|
|
|
@ -17,8 +17,9 @@ import org.qtproject.example.qtabstractitemmodel.QmlModule.Main;
|
|||
public class MainActivity extends AppCompatActivity implements QtQmlStatusChangeListener {
|
||||
private static final String TAG = "QtAIM MainActivity";
|
||||
private Main m_mainQmlComponent;
|
||||
//! [1]
|
||||
private final MyDataModel m_model = new MyDataModel();
|
||||
|
||||
//! [1]
|
||||
@Override
|
||||
protected void onCreate(Bundle savedInstanceState) {
|
||||
super.onCreate(savedInstanceState);
|
||||
|
@ -33,11 +34,12 @@ public class MainActivity extends AppCompatActivity implements QtQmlStatusChange
|
|||
FrameLayout qmlFrameLayout = findViewById(R.id.qmlFrame);
|
||||
qmlFrameLayout.addView(qtQuickView, params);
|
||||
|
||||
//! [2]
|
||||
Button addRowAtEndButton = findViewById(R.id.addRowAtEndButton);
|
||||
Button removeRowFromEndButton = findViewById(R.id.removeRowFromEndButton);
|
||||
Button addColumnButton = findViewById(R.id.addColumnButton);
|
||||
Button removeLastColumnButton = findViewById(R.id.removeLastColumnButton);
|
||||
// Calls in a context of a Android thread.
|
||||
// Calls in a context of a Android main thread.
|
||||
addRowAtEndButton.setOnClickListener(view -> {
|
||||
m_model.addRow();
|
||||
});
|
||||
|
@ -50,17 +52,22 @@ public class MainActivity extends AppCompatActivity implements QtQmlStatusChange
|
|||
removeLastColumnButton.setOnClickListener(view -> {
|
||||
m_model.removeColumn();
|
||||
});
|
||||
//! [2]
|
||||
|
||||
//! [3]
|
||||
qtQuickView.loadComponent(m_mainQmlComponent);
|
||||
//! [3]
|
||||
|
||||
}
|
||||
|
||||
//! [4]
|
||||
@Override
|
||||
public void onStatusChanged(QtQmlStatus qtQmlStatus) {
|
||||
Log.i(TAG, "Status of QtQuickView: " + qtQmlStatus);
|
||||
if (qtQmlStatus == QtQmlStatus.READY)
|
||||
// Calls in a context of a Android thread.
|
||||
// Calls in a context of a Android main thread.
|
||||
m_mainQmlComponent.setDataModel(m_model);
|
||||
}
|
||||
//! [4]
|
||||
|
||||
}
|
||||
|
|
|
@ -28,22 +28,25 @@ public class MyDataModel extends QtAbstractItemModel {
|
|||
private static final int ROLE_COLUMN = 1;
|
||||
private static final int MAX_ROWS_AND_COLUMNS = 26;
|
||||
private int m_columns = 4;
|
||||
/* Two dimensional array of Cell objects to represent a sheet.
|
||||
/*
|
||||
* Two dimensional array of Cell objects to represent a sheet.
|
||||
* First dimension are rows. Second dimension are columns.
|
||||
* TODO QTBUG-127467
|
||||
*/
|
||||
private final ArrayList<ArrayList<Cell>> m_dataList = new ArrayList<>();
|
||||
private final char m_firstLatinLetter = 'A';
|
||||
|
||||
// Called in Android Thread context
|
||||
/*
|
||||
* 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
|
||||
*/
|
||||
//! [1]
|
||||
/*
|
||||
* 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() {
|
||||
//! [1]
|
||||
final int initializingRowAndColumnCount = m_columns;
|
||||
for (int rows = 0 ; rows < initializingRowAndColumnCount; rows++) {
|
||||
ArrayList<Cell> newRow = new ArrayList<>();
|
||||
|
@ -56,18 +59,33 @@ public class MyDataModel extends QtAbstractItemModel {
|
|||
}
|
||||
}
|
||||
|
||||
//! [2]
|
||||
/*
|
||||
* Returns the count of columns.
|
||||
* Threading: called in Android main thread context.
|
||||
* Threading: called in Qt qtMainLoopThread thread context.
|
||||
*/
|
||||
@Override
|
||||
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
|
||||
public int rowCount(QtModelIndex qtModelIndex) {
|
||||
return m_dataList.size();
|
||||
}
|
||||
//! [2]
|
||||
|
||||
// Returns the data to QML based on the roleNames
|
||||
// Called in QML Rendering Thread context
|
||||
//! [3]
|
||||
/*
|
||||
* Returns the data to QML based on the roleNames
|
||||
* Threading: called in Qt qtMainLoopThread thread context.
|
||||
*/
|
||||
@Override
|
||||
public Object data(QtModelIndex qtModelIndex, int role) {
|
||||
switch (role) {
|
||||
|
@ -84,7 +102,11 @@ public class MyDataModel extends QtAbstractItemModel {
|
|||
return null;
|
||||
}
|
||||
}
|
||||
// Function which defines what string in QML side can be used to get the data from Java side
|
||||
|
||||
/*
|
||||
* Defines what string i.e. role in QML side gets the data from Java side.
|
||||
* Threading: called in Qt qtMainLoopThread thread context.
|
||||
*/
|
||||
@Override
|
||||
public HashMap<Integer, String> roleNames() {
|
||||
HashMap<Integer, String> roles = new HashMap<>();
|
||||
|
@ -93,18 +115,30 @@ public class MyDataModel extends QtAbstractItemModel {
|
|||
return roles;
|
||||
}
|
||||
|
||||
/*
|
||||
* Returns a new index model.
|
||||
* Threading: called in Qt qtMainLoopThread thread context.
|
||||
*/
|
||||
@Override
|
||||
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
|
||||
public QtModelIndex parent(QtModelIndex qtModelIndex) {
|
||||
return new QtModelIndex();
|
||||
}
|
||||
//! [3]
|
||||
|
||||
// Four model side calls for MainActivity
|
||||
// Called in Android Thread context
|
||||
//! [4]
|
||||
/*
|
||||
* Adds a row.
|
||||
* Threading: called in Android main thread context.
|
||||
*/
|
||||
public void addRow() {
|
||||
if (m_columns > 0 && m_dataList.size() < MAX_ROWS_AND_COLUMNS) {
|
||||
beginInsertRows(new QtModelIndex(), m_dataList.size(), m_dataList.size());
|
||||
|
@ -113,7 +147,10 @@ public class MyDataModel extends QtAbstractItemModel {
|
|||
}
|
||||
}
|
||||
|
||||
// Called in Android Thread context
|
||||
/*
|
||||
* Removes a row.
|
||||
* Threading: called in Android main thread context.
|
||||
*/
|
||||
public void removeRow() {
|
||||
if (m_dataList.size() > 1) {
|
||||
beginRemoveRows(new QtModelIndex(), m_dataList.size() - 1, m_dataList.size() - 1);
|
||||
|
@ -121,8 +158,13 @@ public class MyDataModel extends QtAbstractItemModel {
|
|||
endRemoveRows();
|
||||
}
|
||||
}
|
||||
//! [4]
|
||||
|
||||
// Called in Android Thread context
|
||||
//! [5]
|
||||
/*
|
||||
* Adds a column.
|
||||
* Threading: called in Android main thread context.
|
||||
*/
|
||||
public void addColumn() {
|
||||
if (!m_dataList.isEmpty() && m_columns < MAX_ROWS_AND_COLUMNS) {
|
||||
beginInsertColumns(new QtModelIndex(), m_columns, m_columns);
|
||||
|
@ -132,7 +174,10 @@ public class MyDataModel extends QtAbstractItemModel {
|
|||
}
|
||||
}
|
||||
|
||||
// Called in Android Thread context
|
||||
/*
|
||||
* Removes a column.
|
||||
* Threading: called in Android main thread context.
|
||||
*/
|
||||
public void removeColumn() {
|
||||
if (m_columns > 1) {
|
||||
int columnToRemove = m_columns - 1;
|
||||
|
@ -141,6 +186,7 @@ public class MyDataModel extends QtAbstractItemModel {
|
|||
endRemoveColumns();
|
||||
}
|
||||
}
|
||||
//! [5]
|
||||
|
||||
private void generateColumn() {
|
||||
int amountOfRows = m_dataList.size();
|
||||
|
|
Loading…
Reference in New Issue