使用QML时,许多Qt开发人员倾向于认为他们至少应该使用C ++对模型进行编码。实际情况不是这样的:QML经过优化,可以与C ++无缝集成。
所有QML代码都将编译为具有Native性能的本机代码。并且在使用Qt Quick编译器时,这已经在应用程序的编译期间发生。QML ListModel提供了一个简单的API,并且可以与ListView,GridView或Repeater之类的视图类型完美配合。
QML最佳实践:为您的ListView使用REST服务和JSON
将QML和JavaScript作为主要的编码语言还有另一个很大的优势:使用REST服务和JSON数据非常容易。
QML的应用程序逻辑是用JavaScript编写的。因此,QML具有内置支持来处理各种数据类型,例如JSON结构。通过一个简单的HTTP请求,您可以获取JSON数据并在视图中使用它。
以下代码片段从REST服务检索并显示了待办事项列表:
import Felgo 3.0
import QtQuick 2.0
App {
// on app start: fetch data from REST api
Component.onCompleted: {
HttpRequest.get("https://jsonplaceholder.typicode.com/todos")
.timeout(5000)
.then(function(res) { dataFetched(res.body) })
.catch(function(err) { console.err("Fetch failed:"+err) });
}
// dataFetched gets called on success of http request
function dataFetched(jsonData) {
listView.model = jsonData // set retrieved json data as model for list
}
// list page
NavigationStack {
Page {
title: "Todo List"
AppListView {
id: listView
anchors.fill: parent
delegate: SimpleRow {
text: modelData.title
}
}
}
}
}
作为ListModel的替代,QML还允许将JSON数据结构分配为模型。使用REST服务时,这非常方便,可以直接使用检索到的JSON结果显示数据。
将JSON数据用于ListView模型的缺点
JSON结构不是ListModel,因此也不是QAbstractListModel实现。在这种情况下,您将无法获得高性能C ++模型的好处。另外,JSON数组是一种变体类型。因此,该列表不能期望使用已定义的数据结构创建其元素。
这导致性能和可用性方面的缺陷。例如,当您更改或替换JSON数据时。然后,视图再次解析整个模型,并从头开始重新绘制所有项目。完全重绘可能需要一段时间,并且用户会注意到。您还会丢失当前的滚动位置,并且不支持高级功能,例如过渡动画。
此时,您可能会想到:让我们创建一个ListModel并用JSON数据填充它。在解决性能问题的同时,又迅速出现了另一个问题:以后再从API获取新数据时,如何更新模型?
如果从头开始重新创建ListModel,则视图再次需要完全重绘。为了避免这种情况,我们可以将最新JSON数据的每个条目与ListModel的当前状态进行比较。这仅允许将更改的或新的数据条目同步到模型。
但是,这样的手动同步需要大量额外的工作。它还会带来一些开销,尤其是当您开始比较大型数据集时。JavaScript和QML不太适合执行此类数据密集型操作。
但是请放心: QML的JsonListModel组件这一特殊的ListModel的实现,可处理JSON数据,完美解决了该问题。
JsonListModel工作机制
该JsonListModel提供了一个简单的方法来转换JSON数据到一个QML的ListModel与使用例如一个AppListView。这是将模型用于待办事项列表示例的方式:
import Felgo 3.0
import QtQuick 2.0
App {
// on app start: fetch data from REST api
Component.onCompleted: {
HttpRequest.get("https://jsonplaceholder.typicode.com/todos")
.timeout(5000)
.then(function(res) { dataFetched(res.body) })
.catch(function(err) { console.err("Fetch failed:"+err) });
}
// dataFetched gets called on success of http request
function dataFetched(jsonData) {
listView.jsonData = jsonData // set retrieved json data as model for list
}
// list page
NavigationStack {
Page {
title: "Todo List"
AppListView {
id: listView
anchors.fill: parent
// property for json data, used as source for JsonListModel
property var jsonData: []
// use JsonListModel as model
model: JsonListModel {
source: listView.jsonData
keyField: "id"
fields: ["id", "title"]
}
// delegate
delegate: SimpleRow {
text: title
}
}
}
}
}
该JsonListModel将保存指定的JSON数据的本地副本。只要JSON源发生了变化,数据就会与列表模型的本地副本进行比较。为了标识每个唯一的数据记录,重要的是指定数据对象的keyField。在对新旧数据集进行比较之后,JsonListModel会分别应用所有检测到的更改。因此,它将逐步将模型与更改后的JSON数据同步。
该JsonListModel类型实现了全QML的ListModel API以及分别发送所有更改的事件。因此,列表视图只能更新相关条目或应用过渡动画。这非常有用,因为您可以例如获取新数据并只需替换旧的JSON。该JsonListModel将检测所有更改,并ListView的更新改变了相应的项目-没有完全重绘。
JsonListModel的好处
因此,这样可以获得更好的性能,并且在列表更新时滚动保持流畅:
总而言之,使用JsonListModel可以:
- 使用QML和JavaScript从REST API获取JSON数据。
- 将您的JSON传递给模型,该模型将数据同步到ListModel并为在视图中使用做好准备。
- 在您的QML视图中显示模型数据,仅更新已更改的项目。
使用REST API和JSON数据时,无需使用C ++创建自定义模型。该JsonListModel本身就是你的C ++模型。它可以从QML完全使用,并且可以与任何格式的JSON对象一起使用:
除了列表视图之外,JsonListModel还支持GridView和Repeater类型来显示模型数据。它是由Ben Lau基于QSyncable实现的可同步C ++模型。可以在GitHub上找到完整的项目。
该JsonListModel还可以提供免费的Felgo SDK。上例中使用的Felgo Apps模块包含许多此类有用的组件。Felgo致力于尽可能简化Qt的移动应用程序开发。例如,用于网络的HttpRequest类型或诸如NavigationStack的UI类型也是SDK的一部分。
使用SortFilterProxyModel动态排序或过滤数据
为了排序和过滤列表模型,C ++ Qt提供了QSortFilterProxyModel类。同样,您可以将SortFilterProxyModel QML类型与QML中的任何列表模型一起使用。它还可以与JsonListModel结合使用:
import Felgo 3.0
import QtQuick 2.0
App {
Page {
id: page
// property with json data
property var jsonData: [
{
"id": 1,
"title": "Apple",
"type": "Fruit"
},
{
"id": 2,
"title": "Ham",
"type": "Meat"
},
{
"id": 3,
"title": "Bacon",
"type": "Meat"
},
{
"id": 4,
"title": "Banana",
"type": "Fruit"
}
]
// list model for json data
JsonListModel {
id: jsonModel
source: page.jsonData
keyField: "id"
fields: ["id", "title", "type"]
}
// SortFilterProxyModel for sorting or filtering lists
SortFilterProxyModel {
id: sortedModel
// use the Component.onCompleted handler to configure SortFilterProxyModel for JsonListModel
Component.onCompleted: sourceModel = jsonModel
// add a sorter to sort the list by type
sorters: StringSorter { id: typeSorter; roleName: "type"; ascendingOrder: true }
}
// list view
AppListView {
anchors.fill: parent
model: sortedModel
delegate: SimpleRow {
text: model.title
}
section.property: "type"
section.delegate: SimpleSection { }
}
// Button change the sorting order
AppButton {
anchors.horizontalCenter: parent.horizontalCenter
anchors.bottom: parent.bottom
text: "Change Order"
onClicked: typeSorter.ascendingOrder = !typeSorter.ascendingOrder
}
} // Page
}
本示例显示了不同水果和肉类条目的列表。列表条目使用SortFilterProxyModel按类型排序。您还可以通过按AppButton更改排序顺序。
与JsonListModel相似,SortFilterProxyModel是暴露给QML的C ++类型。因此,对于这个简单的QML示例,所有与数据和模型相关的任务实际上都是由C ++类型执行的。使用REST服务和JSON数据时,您可以完全处理所有模型并查看QML中的代码!