Android: Add documentation for QtAbstractListModel example
This change includes code snippet markers, some minimal code changes (moving code around for snippet clarity), a screenshot of the example and the main documentation page for the example. Task-number: QTBUG-126841 Pick-to: 6.8 Change-Id: I2a10571f37a70a55b8411f1b7989b47810639386 Reviewed-by: Nicholas Bennett <nicholas.bennett@qt.io>
This commit is contained in:
parent
7afacee589
commit
50aea1f6a3
Binary file not shown.
After Width: | Height: | Size: 47 KiB |
|
@ -0,0 +1,131 @@
|
|||
// Copyright (C) 2024 The Qt Company Ltd.
|
||||
// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GFDL-1.3-no-invariants-only
|
||||
|
||||
/*!
|
||||
\page qtabstractlistmoder-kotlin-example.html
|
||||
\title Exposing QtAbstractListModel to QML
|
||||
\brief Uses a \l QtAbstractListModel to share data between Android code and
|
||||
a QML view.
|
||||
\ingroup qtquickexamples
|
||||
|
||||
\section1 Overview
|
||||
|
||||
\image qtabstractlistmodel_portrait.png
|
||||
|
||||
This example consists of two separate projects: A QML project and a Kotlin-based Android
|
||||
project which will host and display the QML content. It shows how to use \l QtAbstractListModel
|
||||
to share data from the Android side to the QML view which displays the data using a
|
||||
\l ListView.
|
||||
|
||||
\section1 Running the example
|
||||
|
||||
To run this example, you need Android Studio and a \l {Qt for Android} installation.
|
||||
|
||||
The \l {Qt Gradle Plugin} will be used to build the QML project during the Android project
|
||||
build process. For this, the example has some plugin configuration in the app-level
|
||||
build.gradle file which may need to be modified if the plugin cannot, for example, find the Qt
|
||||
kit directory.
|
||||
|
||||
\snippet android/models/qtabstractlistmodel_kotlin/app/build.gradle build.gradle QtBuild config
|
||||
|
||||
For further configuration of the plugin, please refer to the
|
||||
\l [Qt Gradle Plugin]{Configure the plugin}{Qt Gradle Plugin documentation}.
|
||||
|
||||
\section1 QML project
|
||||
|
||||
The QML project is quite simple, it defines a data model as a property of the root object
|
||||
and some UI elements to display the data from that model.
|
||||
|
||||
\snippet android/models/qtabstractlistmodel/Main.qml QML root item and dataModel definition
|
||||
|
||||
To display the data from the model, a ListView is created. The \c model property is then set
|
||||
to the data model declared earlier.
|
||||
|
||||
\snippet android/models/qtabstractlistmodel/Main.qml ListView definition
|
||||
|
||||
In order to display the data model, the ListView needs a delegate which will be instantiated
|
||||
for each item in the data model. In this case, the delegate will be a \l Rectangle that holds
|
||||
two \l Text elements in a \l Column, displaying the data from each element in the data model.
|
||||
|
||||
\snippet android/models/qtabstractlistmodel/Main.qml ListView delegate definition
|
||||
|
||||
\section1 Kotlin project
|
||||
|
||||
The Android side consists of a single \l {Android: Activity} {Activity} and the definition for the data model used
|
||||
earlier in the QML view.
|
||||
|
||||
\section2 Data model
|
||||
|
||||
The data model \c MyListModel is a child class of QtAbstractListModel, with
|
||||
\c ArrayList<String> as the internal storage system for data. In the initializer block of
|
||||
\c MyListModel, it generates some random data for the list.
|
||||
|
||||
\snippet android/models/qtabstractlistmodel_kotlin/app/src/main/java/com/example/qtlm_in_kotlin_based_android_project/MyListModel.kt MyListModel definition
|
||||
|
||||
Each item in the model has a set of data elements associated with it, each with its own role.
|
||||
Custom implementations of QtAbstractItemModel must define a custom role for each data
|
||||
element. Each role has an associated \l {Kotlin: Int} {Int} value, which is used when
|
||||
retrieving the data, and a \l {Kotlin: String} {String} value, which specifies the name of the
|
||||
data element when used from QML.
|
||||
|
||||
\snippet android/models/qtabstractlistmodel_kotlin/app/src/main/java/com/example/qtlm_in_kotlin_based_android_project/MyListModel.kt MyListModel::roleNames
|
||||
|
||||
While the \c Int values in the \c "roleNames()" method may be hard-coded, this example
|
||||
specifies a custom enum class \c DataRole within \c MyListModel, which is used when referring
|
||||
to these values. In this example, we define two roles: UUID and Row.
|
||||
|
||||
\snippet android/models/qtabstractlistmodel_kotlin/app/src/main/java/com/example/qtlm_in_kotlin_based_android_project/MyListModel.kt MyListModel::DataRole enum
|
||||
|
||||
When it comes to returning data from the data model, the class must override the
|
||||
\c "QtAbstractListModel::data()" method. This method takes two parameters: \l QtModelIndex and
|
||||
\c Int, which refer to the index and role of the data element, respectively.
|
||||
|
||||
In \c "MyDataModel::data()", the \c UUID role returns the data from the given index in the
|
||||
internal data, while the \c Row role returns the row of the requested element.
|
||||
|
||||
\note This method, along with some others, is annotated with a
|
||||
\l {Kotlin: Synchronized} {@Synchronized} tag. This is due to calls to these methods
|
||||
originating from the Qt thread and accessing the underlying data possibly at the same time as
|
||||
requests from the Android thread via the \c "addRow()" and \c "removeRow()" methods.
|
||||
|
||||
\snippet android/models/qtabstractlistmodel_kotlin/app/src/main/java/com/example/qtlm_in_kotlin_based_android_project/MyListModel.kt MyListModel::data
|
||||
|
||||
To allow outside actors to manipulate the QtAbstractItemModel, the example adds two additional
|
||||
methods to \c MyDataModel. To add data to the row, it has the \c "addRow()" method; to remove
|
||||
data, there is the \c "removeRow()" method. These are used from the main activity.
|
||||
|
||||
\snippet android/models/qtabstractlistmodel_kotlin/app/src/main/java/com/example/qtlm_in_kotlin_based_android_project/MyListModel.kt MyListModel::addRow
|
||||
|
||||
\snippet android/models/qtabstractlistmodel_kotlin/app/src/main/java/com/example/qtlm_in_kotlin_based_android_project/MyListModel.kt MyListModel::removeRow
|
||||
|
||||
\section2 Main activity
|
||||
|
||||
The \c MainActivity class is a simple Kotlin-based Activity but also implements the
|
||||
interface \c QtQmlStatusChangeListener to listen to QML loading status events. It also
|
||||
stores the \c QtQmlComponent object for the main view of the QML application and an instance
|
||||
of the data model detailed above.
|
||||
|
||||
\snippet android/models/qtabstractlistmodel_kotlin/app/src/main/java/com/example/qtlm_in_kotlin_based_android_project/MainActivity.kt MainActivity definition
|
||||
|
||||
When creating the main Activity of the application, the example first creates a \l QtQuickView
|
||||
and places it into the view hierarchy.
|
||||
|
||||
\snippet android/models/qtabstractlistmodel_kotlin/app/src/main/java/com/example/qtlm_in_kotlin_based_android_project/MainActivity.kt Adding QtQuickView
|
||||
|
||||
After adding the QtQuickView into the UI, the example finds the buttons that are used to
|
||||
manipulate the data model and sets some click listeners to call \c addRow() and \c removeRow()
|
||||
on the member data model.
|
||||
|
||||
\snippet android/models/qtabstractlistmodel_kotlin/app/src/main/java/com/example/qtlm_in_kotlin_based_android_project/MainActivity.kt Adding control buttons
|
||||
|
||||
Once the UI setup and listeners are done, the QML component can be prepared and loaded. The
|
||||
example sets the \c MainActivity as a listener for the status change signal of the QML
|
||||
component and tells \c QtQuickView to load the QML component.
|
||||
|
||||
\snippet android/models/qtabstractlistmodel_kotlin/app/src/main/java/com/example/qtlm_in_kotlin_based_android_project/MainActivity.kt Loading the QML component
|
||||
|
||||
Finally, once the QML component is successfully loaded, the example assigns the value of the
|
||||
MyDataModel instance into the \c dataModel property in the QML component.
|
||||
|
||||
\snippet android/models/qtabstractlistmodel_kotlin/app/src/main/java/com/example/qtlm_in_kotlin_based_android_project/MainActivity.kt Linking the data model
|
||||
*/
|
|
@ -3,19 +3,23 @@
|
|||
import QtQuick
|
||||
import QtQuick.Controls
|
||||
|
||||
//! [QML root item and dataModel definition]
|
||||
Rectangle {
|
||||
id: mainRectangle
|
||||
|
||||
property AbstractItemModel dataModel
|
||||
//! [QML root item and dataModel definition]
|
||||
|
||||
color: "#00414A"
|
||||
border.width: 2
|
||||
border.color: "black"
|
||||
|
||||
//! [ListView definition]
|
||||
ListView {
|
||||
id: listView
|
||||
|
||||
model: mainRectangle.dataModel
|
||||
//! [ListView definition]
|
||||
ScrollBar.vertical: ScrollBar {}
|
||||
spacing: 10
|
||||
|
||||
|
@ -24,6 +28,7 @@ Rectangle {
|
|||
margins: 20
|
||||
}
|
||||
|
||||
//! [ListView delegate definition]
|
||||
delegate: Rectangle {
|
||||
required property var model
|
||||
|
||||
|
@ -65,5 +70,6 @@ Rectangle {
|
|||
}
|
||||
}
|
||||
}
|
||||
//! [ListView delegate definition]
|
||||
}
|
||||
}
|
||||
|
|
|
@ -4,11 +4,13 @@ plugins {
|
|||
id 'org.qtproject.qt.gradleplugin' version '0.1-SNAPSHOT+'
|
||||
}
|
||||
|
||||
//! [build.gradle QtBuild config]
|
||||
QtBuild {
|
||||
// Relative for pre-installed (Installer or MaintenanceTool) installations.
|
||||
qtPath file('../../../../../../../6.8.0')
|
||||
projectPath file('../../qtabstractlistmodel')
|
||||
}
|
||||
//! [build.gradle QtBuild config]
|
||||
|
||||
android {
|
||||
namespace 'com.example.qtabstractlistmodel_kotlin'
|
||||
|
|
|
@ -14,23 +14,25 @@ import org.qtproject.qt.android.QtQmlStatus
|
|||
import org.qtproject.qt.android.QtQmlStatusChangeListener
|
||||
import org.qtproject.qt.android.QtQuickView
|
||||
|
||||
//! [MainActivity definition]
|
||||
class MainActivity : AppCompatActivity(), QtQmlStatusChangeListener {
|
||||
private val m_mainQmlComponent: Main = Main()
|
||||
private val m_listModel = MyListModel()
|
||||
//! [MainActivity definition]
|
||||
|
||||
override fun onCreate(savedInstanceState: Bundle?) {
|
||||
super.onCreate(savedInstanceState)
|
||||
setContentView(R.layout.activity_main)
|
||||
|
||||
//! [Adding QtQuickView]
|
||||
val qtQuickView: QtQuickView = QtQuickView(this)
|
||||
m_mainQmlComponent.setStatusChangeListener(this)
|
||||
|
||||
val params: ViewGroup.LayoutParams = FrameLayout.LayoutParams(
|
||||
ViewGroup.LayoutParams.MATCH_PARENT, ViewGroup.LayoutParams.MATCH_PARENT
|
||||
)
|
||||
val qmlFrameLayout: FrameLayout = findViewById<FrameLayout>(R.id.qmlFrame)
|
||||
qmlFrameLayout.addView(qtQuickView, params)
|
||||
|
||||
//! [Adding QtQuickView]
|
||||
//! [Adding control buttons]
|
||||
val addRowAtEndButton: Button = findViewById<Button>(R.id.addRow)
|
||||
val removeRowFromEndButton: Button = findViewById<Button>(R.id.removeRow)
|
||||
addRowAtEndButton.setOnClickListener { _: View? ->
|
||||
|
@ -39,13 +41,18 @@ class MainActivity : AppCompatActivity(), QtQmlStatusChangeListener {
|
|||
removeRowFromEndButton.setOnClickListener { _: View? ->
|
||||
m_listModel.removeRow()
|
||||
}
|
||||
|
||||
//! [Adding control buttons]
|
||||
//! [Loading the QML component]
|
||||
m_mainQmlComponent.setStatusChangeListener(this)
|
||||
qtQuickView.loadComponent(m_mainQmlComponent)
|
||||
//! [Loading the QML component]
|
||||
}
|
||||
|
||||
//! [Linking the data model]
|
||||
override fun onStatusChanged(qtQmlStatus: QtQmlStatus) {
|
||||
if (qtQmlStatus === QtQmlStatus.READY) {
|
||||
m_mainQmlComponent.setDataModel(m_listModel)
|
||||
}
|
||||
}
|
||||
//! [Linking the data model]
|
||||
}
|
||||
|
|
|
@ -7,7 +7,66 @@ import java.util.UUID
|
|||
import org.qtproject.qt.android.QtAbstractListModel
|
||||
import org.qtproject.qt.android.QtModelIndex
|
||||
|
||||
//! [MyListModel definition]
|
||||
class MyListModel : QtAbstractListModel() {
|
||||
private val m_dataList = ArrayList<String>()
|
||||
|
||||
init {
|
||||
synchronized(this) {
|
||||
for (row in 0..4) {
|
||||
m_dataList.add(UUID.randomUUID().toString())
|
||||
}
|
||||
}
|
||||
}
|
||||
//! [MyListModel definition]
|
||||
|
||||
//! [MyListModel::data]
|
||||
@Synchronized
|
||||
override fun data(qtModelIndex: QtModelIndex, role: Int): Any {
|
||||
return when (DataRole.valueOf(role)) {
|
||||
DataRole.UUID -> "UUID: " + m_dataList[qtModelIndex.row()]
|
||||
DataRole.Row -> "Row: " + qtModelIndex.row()
|
||||
else -> ""
|
||||
}
|
||||
}
|
||||
//! [MyListModel::data]
|
||||
|
||||
@Synchronized
|
||||
override fun rowCount(qtModelIndex: QtModelIndex?): Int {
|
||||
return m_dataList.size
|
||||
}
|
||||
|
||||
//! [MyListModel::roleNames]
|
||||
@Synchronized
|
||||
override fun roleNames(): HashMap<Int, String> {
|
||||
val m_roles = HashMap<Int, String>()
|
||||
m_roles[DataRole.UUID.value()] = "id"
|
||||
m_roles[DataRole.Row.value()] = "row"
|
||||
return m_roles
|
||||
}
|
||||
//! [MyListModel::roleNames]
|
||||
|
||||
//! [MyListModel::addRow]
|
||||
@Synchronized
|
||||
fun addRow() {
|
||||
beginInsertRows(QtModelIndex(), m_dataList.size, m_dataList.size)
|
||||
m_dataList.add(UUID.randomUUID().toString())
|
||||
endInsertRows()
|
||||
}
|
||||
//! [MyListModel::addRow]
|
||||
|
||||
//! [MyListModel::removeRow]
|
||||
@Synchronized
|
||||
fun removeRow() {
|
||||
if (!m_dataList.isEmpty()) {
|
||||
beginRemoveRows(QtModelIndex(), m_dataList.size - 1, m_dataList.size - 1)
|
||||
m_dataList.removeAt(m_dataList.size - 1)
|
||||
endRemoveRows()
|
||||
}
|
||||
}
|
||||
//! [MyListModel::removeRow]
|
||||
|
||||
//! [MyListModel::DataRole enum]
|
||||
private enum class DataRole(val m_value: Int) {
|
||||
UUID(0),
|
||||
Row(1);
|
||||
|
@ -24,52 +83,5 @@ class MyListModel : QtAbstractListModel() {
|
|||
}
|
||||
}
|
||||
}
|
||||
|
||||
private val m_dataList = ArrayList<String>()
|
||||
|
||||
init {
|
||||
synchronized(this) {
|
||||
for (row in 0..4) {
|
||||
m_dataList.add(UUID.randomUUID().toString())
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@Synchronized
|
||||
override fun data(qtModelIndex: QtModelIndex, role: Int): Any {
|
||||
return when (DataRole.valueOf(role)) {
|
||||
DataRole.UUID -> "UUID: " + m_dataList[qtModelIndex.row()]
|
||||
DataRole.Row -> "Row: " + qtModelIndex.row()
|
||||
else -> ""
|
||||
}
|
||||
}
|
||||
|
||||
@Synchronized
|
||||
override fun rowCount(qtModelIndex: QtModelIndex?): Int {
|
||||
return m_dataList.size
|
||||
}
|
||||
|
||||
@Synchronized
|
||||
override fun roleNames(): HashMap<Int, String> {
|
||||
val m_roles = HashMap<Int, String>()
|
||||
m_roles[DataRole.UUID.value()] = "id"
|
||||
m_roles[DataRole.Row.value()] = "row"
|
||||
return m_roles
|
||||
}
|
||||
|
||||
@Synchronized
|
||||
fun addRow() {
|
||||
beginInsertRows(QtModelIndex(), m_dataList.size, m_dataList.size)
|
||||
m_dataList.add(UUID.randomUUID().toString())
|
||||
endInsertRows()
|
||||
}
|
||||
|
||||
@Synchronized
|
||||
fun removeRow() {
|
||||
if (!m_dataList.isEmpty()) {
|
||||
beginRemoveRows(QtModelIndex(), m_dataList.size - 1, m_dataList.size - 1)
|
||||
m_dataList.removeAt(m_dataList.size - 1)
|
||||
endRemoveRows()
|
||||
}
|
||||
}
|
||||
//! [MyListModel::DataRole enum]
|
||||
}
|
||||
|
|
|
@ -81,4 +81,15 @@
|
|||
\externalpage https://developer.android.com/reference/android/Manifest.permission#SYSTEM_ALERT_WINDOW
|
||||
\title Android: SYSTEM_ALERT_WINDOW
|
||||
*/
|
||||
|
||||
/*!
|
||||
\externalpage https://developer.android.com/reference/android/app/Activity
|
||||
\title Android: Activity
|
||||
*/
|
||||
/*!
|
||||
\externalpage https://kotlinlang.org/api/latest/jvm/stdlib/kotlin/-int/
|
||||
\title Kotlin: Int
|
||||
*/
|
||||
/*!
|
||||
\externalpage https://kotlinlang.org/api/latest/jvm/stdlib/kotlin/-string/
|
||||
\title Kotlin: String
|
||||
*/
|
||||
|
|
Loading…
Reference in New Issue