简单地说,应用程序需要形成数据并显示数据。Qt Quick有模型、视图和委托的概念来显示数据。它们将数据的可视化模块化,以便让开发者或设计者对数据的不同方面进行控制。开发者可以将列表视图换成网格视图,而对数据没有什么改变。同样,将数据的实例封装在一个委托中,允许开发者决定如何呈现或处理数据。
为了使数据可视化,将视图的模型属性与模型绑定,将委托属性与组件或其他兼容类型绑定。
视图是项目集合的容器。它们具有丰富的功能,并且可以定制,以满足风格或行为要求。
在Qt Quick图形类型的基本集合中提供了一组标准视图:
这些类型有每个类型独有的属性和行为。
视图允许通过装饰属性(如页眉、页脚和章节属性)进行视觉定制。通过绑定一个对象,通常是另一个视觉对象,到这些属性,视图是可装饰的。一个页脚可能包括一个展示边框的矩形类型,或者一个在列表顶部显示标志的页眉。
假设一个特定的俱乐部想用它的品牌颜色来装饰它的会员列表。一个会员列表在一个模型中,委托将显示模型的内容。
- ListModel {
- id: nameModel
- ListElement { name: "Alice" }
- ListElement { name: "Bob" }
- ListElement { name: "Jane" }
- ListElement { name: "Harry" }
- ListElement { name: "Wendy" }
- }
- Component {
- id: nameDelegate
- Text {
- text: name;
- font.pixelSize: 24
- }
- }
俱乐部可以通过将视觉对象绑定到header和footer属性来装饰成员列表。视觉对象可以被定义在内联,在另一个文件中,或在一个组件类型Component中。
- ListView {
- anchors.fill: parent
- clip: true
- model: nameModel
- delegate: nameDelegate
- header: bannercomponent
- footer: Rectangle {
- width: parent.width; height: 30;
- gradient: clubcolors
- }
- highlight: Rectangle {
- width: parent.width
- color: "lightgray"
- }
- }
-
- Component { //instantiated when header is processed
- id: bannercomponent
- Rectangle {
- id: banner
- width: parent.width; height: 50
- gradient: clubcolors
- border {color: "#9EDDF2"; width: 2}
- Text {
- anchors.centerIn: parent
- text: "Club Members"
- font.pixelSize: 32
- }
- }
- }
- Gradient {
- id: clubcolors
- GradientStop { position: 0.0; color: "#8EE2FE"}
- GradientStop { position: 0.66; color: "#7ED2EE"}
- }
视图处理其内容的拖动和滑动,但它们不处理与各个委托的触摸交互。为了让委托对触摸输入做出反应,例如设置currentIndex,必须由委托提供一个具有适当触摸处理逻辑的MouseArea。
请注意,如果highlightRangeMode被设置为StrictlyEnforceRange,currentIndex将受到拖动/滑动视图的影响,因为视图将始终确保currentIndex在指定的高亮范围内。
ListView的内容可以被分组为部分,相关的列表项根据它们的部分被标记。此外,这些部分可以用代表进行装饰。
列表可以包含一个表示人名和该人所属的团队的列表。
- ListModel {
- id: nameModel
- ListElement { name: "Alice"; team: "Crypto" }
- ListElement { name: "Bob"; team: "Crypto" }
- ListElement { name: "Jane"; team: "QA" }
- ListElement { name: "Victor"; team: "QA" }
- ListElement { name: "Wendy"; team: "Graphics" }
- }
- Component {
- id: nameDelegate
- Text {
- text: name;
- font.pixelSize: 24
- anchors.left: parent.left
- anchors.leftMargin: 2
- }
- }
ListView类型有section附件属性,可以将相邻和相关的类型组合成一个部分。section.property决定了哪种列表类型的属性可以作为section使用。section.criteria可以决定章节名称的显示方式,section.delegate类似于视图的delegate属性。
- ListView {
- anchors.fill: parent
- model: nameModel
- delegate: nameDelegate
- focus: true
- highlight: Rectangle {
- color: "lightblue"
- width: parent.width
- }
- section {
- property: "team"
- criteria: ViewSection.FullString
- delegate: Rectangle {
- color: "#b0dfb0"
- width: parent.width
- height: childrenRect.height + 4
- Text { anchors.horizontalCenter: parent.horizontalCenter
- font.pixelSize: 16
- font.bold: true
- text: section
- }
- }
- }
- }
视图需要一个委托来直观地表示一个列表中的项目。视图将根据委托定义的模板来可视化每个项目列表。一个模型中的项目可以通过索引属性以及项目的属性进行访问。
- Component {
- id: petdelegate
- Text {
- id: label
- font.pixelSize: 24
- text: if (index == 0)
- label.text = type + " (default)"
- else
- text: type
- }
- }
委托人所绑定的列表视图可以通过ListView.view属性从委托那里访问。同样地,GridView的GridView.view对委托也是可用的。因此,相应的模型及其属性可以通过ListView.view.model获得。此外,模型中任何定义的信号或方法也可以被访问。
当你想对一些视图使用同一个委托,但你希望每个视图的装饰或其他功能不同,并且你希望这些不同的设置成为每个视图的属性时,这种机制很有用。同样地,访问或显示模型的一些属性也可能会引起兴趣。
在下面的例子中,委托显示了模型的属性语言,其中一个字段的颜色取决于视图的属性 fruit_color。
- Rectangle {
- width: 200; height: 200
-
- ListModel {
- id: fruitModel
- property string language: "en"
- ListElement {
- name: "Apple"
- cost: 2.45
- }
- ListElement {
- name: "Orange"
- cost: 3.25
- }
- ListElement {
- name: "Banana"
- cost: 1.95
- }
- }
-
- Component {
- id: fruitDelegate
- Row {
- id: fruit
- Text { text: " Fruit: " + name; color: fruit.ListView.view.fruit_color }
- Text { text: " Cost: $" + cost }
- Text { text: " Language: " + fruit.ListView.view.model.language }
- }
- }
-
- ListView {
- property color fruit_color: "green"
- model: fruitModel
- delegate: fruitDelegate
- anchors.fill: parent
- }
- }
数据是通过指定的数据角色提供给委托人的,委托人可以与之绑定。这里是一个有两个角色的ListModel,即type和age,以及一个有委托的ListView,它与这些角色绑定以显示它们的值。
- import QtQuick 2.0
-
- Item {
- width: 200; height: 250
-
- ListModel {
- id: myModel
- ListElement { type: "Dog"; age: 8 }
- ListElement { type: "Cat"; age: 5 }
- }
-
- Component {
- id: myDelegate
- Text { text: type + ", " + age }
- }
-
- ListView {
- anchors.fill: parent
- model: myModel
- delegate: myDelegate
- }
- }
如果模型的属性和委托的属性之间存在命名冲突,可以用合格的模型名称代替角色进行访问。例如,如果Text类型有type或age属性,那么上例中的文本将显示这些属性值,而不是模型项中的type和age值。在这种情况下,这些属性可以被引用为model.type和model.age,以确保委托显示来自模型项的属性值。
一个特殊的索引角色包含了模型中项目的索引,也可以提供给委托人。注意,如果项目从模型中被移除,这个索引将被设置为-1。如果你绑定了索引角色,请确保逻辑上考虑到索引为-1的可能性,也就是说,项目不再有效。(通常项目很快就会被销毁,但是在一些视图中可以通过delayRemove附件属性延迟委托销毁)。
没有命名角色的模型(比如下面所示的ListModel)将通过modelData角色提供数据。modelData 角色也被提供给只有一个角色的模型。在这种情况下,modelData 角色包含与命名角色相同的数据。
QML在内置的QML类型集合中提供了几种类型的数据模型。此外,模型可以用Qt C++创建,然后提供给QQmlEngine供QML组件使用。关于创建这些模型的信息,请访问Using C++ Models with Qt Quick Views和创建QML类型文章。
从一个模型中定位项目可以使用中继器来实现。
ListModel是一个在QML中指定的类型的简单层次结构。可用的角色是由ListElement属性指定的。
- ListModel {
- id: fruitModel
-
- ListElement {
- name: "Apple"
- cost: 2.45
- }
- ListElement {
- name: "Orange"
- cost: 3.25
- }
- ListElement {
- name: "Banana"
- cost: 1.95
- }
- }
上述模型有两个角色,名字和费用。这些可以通过ListView委托来绑定,例如:
-
- ListView {
- anchors.fill: parent
- model: fruitModel
- delegate: Row {
- Text { text: "Fruit: " + name }
- Text { text: "Cost: $" + cost }
- }
- }
ListModel提供了直接通过JavaScript来操作ListModel的方法。在这种情况下,插入的第一个项目决定了使用该模型的任何视图的可用角色。例如,如果一个空的ListModel被创建并通过JavaScript填充,第一次插入提供的角色是将在视图中显示的唯一角色:
- ListModel { id: fruitModel }
- ...
- MouseArea {
- anchors.fill: parent
- onClicked: fruitModel.append({"cost": 5.95, "name":"Pizza"})
- }
当MouseArea被点击时,fruitModel将有两个角色,cost和name。即使后续的角色被添加,也只有前两个角色会被使用该模型的视图处理。要重置模型中的可用角色,请调用ListModel::clear()。
XmlListModel允许从一个XML数据源构建一个模型。角色是通过XmlRole类型指定的。该类型需要被导入。
import QtQuick.XmlListModel 2.0
下面的模型有三个角色,标题、链接和描述。
- XmlListModel {
- id: feedModel
- source: "http://rss.news.yahoo.com/rss/oceania"
- query: "/rss/channel/item"
- XmlRole { name: "title"; query: "title/string()" }
- XmlRole { name: "link"; query: "link/string()" }
- XmlRole { name: "description"; query: "description/string()" }
- }
查询属性指定XmlListModel为XML文档中的每个
RSS新闻演示展示了XmlListModel如何被用来显示RSS提要。
ObjectModel包含要在视图中使用的可视化项目。当ObjectModel被用于视图时,视图不需要委托,因为ObjectModel已经包含了可视化的委托(项目)。
下面的例子在一个ListView中放置了三个彩色的矩形:
- import QtQuick 2.0
- import QtQml.Models 2.1
-
- Rectangle {
- ObjectModel {
- id: itemModel
- Rectangle { height: 30; width: 80; color: "red" }
- Rectangle { height: 30; width: 80; color: "green" }
- Rectangle { height: 30; width: 80; color: "blue" }
- }
-
- ListView {
- anchors.fill: parent
- model: itemModel
- }
- }
注意:VisualItemModel也可以被使用,但它只是为了兼容性的原因而提供。VisualItemModel允许一个QML项目作为模型被提供。这个模型包含数据和委托;VisualItemModel的子项目提供委托的内容。该模型不提供任何角色。
一个整数可以作为一个包含一定数量类型的模型。在这种情况下,该模型没有任何数据角色。
下面的例子创建了一个有五个元素的ListView。
一个对象实例可以用来指定一个具有单一对象类型的模型。对象的属性被作为角色提供。
下面的例子创建了一个有一个项目的列表,显示myText文本的颜色。注意使用完全合格的model.color属性以避免与委托中Text类型的color属性发生冲突。
- Rectangle {
- width: 200; height: 250
-
- Text {
- id: myText
- text: "Hello"
- color: "#dd44ee"
- }
-
- Component {
- id: myDelegate
- Text { text: model.color }
- }
-
- ListView {
- anchors.fill: parent
- anchors.topMargin: 30
- model: myText
- delegate: myDelegate
- }
- }
模型可以用C++定义,然后提供给QML使用。这种机制对于将现有的C++数据模型或其他复杂的数据集暴露给QML很有用。
有关信息,请访问使用C++模型与Qt快速浏览文章。
中继器使用模型中的数据,从一个模板中创建项目,供定位器使用。结合中继器和定位器是一个简单的方法来布置大量的项目。一个中继器项目被放置在一个定位器内,并生成包围着的定位器所安排的项目。
每个中继器通过将使用模型属性指定的模型中的每个数据元素与定义为中继器内子项目的模板项目结合起来,创建若干项目。项目的总数是由模型中的数据量决定的。
下面的例子显示了一个与网格项一起使用的中继器,用来排列一组矩形项。中继器项创建了一系列24个矩形,供网格项以5乘5的排列方式定位。
- import QtQuick 2.0
-
- Rectangle {
- width: 400; height: 400; color: "black"
-
- Grid {
- x: 5; y: 5
- rows: 5; columns: 5; spacing: 10
-
- Repeater { model: 24
- Rectangle { width: 70; height: 70
- color: "lightgreen"
-
- Text { text: index
- font.pointSize: 30
- anchors.centerIn: parent } }
- }
- }
- }
中继器创建的项目数量由其count属性持有。不可能通过设置这个属性来确定要创建的项目数量。相反,像上面的例子一样,我们使用一个整数作为模型。
更多细节,请看QML数据模型文档。
如果模型是一个字符串列表,该委托也会暴露在一个只读的modelData属性中,该属性持有字符串。例如:
转场可以用来为添加到定位器、在定位器中移动或从定位器中移除的项目制作动画。
添加项目的转场适用于作为定位器的一部分而创建的项目,以及那些被重新赋权成为定位器的子项目。
移除项目的过渡适用于在定位器中被删除的项目,以及从定位器中移除并在文档中获得新父项的项目。
注意:将项目的不透明度改变为零不会导致它们从定位器中消失。它们可以通过改变可见属性被删除和重新添加。