|
@@ -0,0 +1,270 @@
|
|
|
+# 高级用法(Advanced Techniques)
|
|
|
+
|
|
|
+## 6.5.1 路径视图(The PathView)
|
|
|
+
|
|
|
+路径视图(PathView)非常强大,但也非常复杂,这个视图由QtQuick提供。它创建了一个可以让子项沿着任意路径移动的视图。沿着相同的路径,使用缩放(scale),透明(opacity)等元素可以更加详细的控制过程。
|
|
|
+
|
|
|
+当使用路径视图(PathView)时,你必须定义一个代理和一个路径。在这些之上,路径视图(PathView)本身也可以自定义一些属性的区间。通常会使用pathItemCount属性,它控制了一次可见的子项总数。preferredHighLightBegin属性控制了高亮区间,preferredHighlightEnd与highlightRangeMode,控制了当前项怎样沿着路径显示。
|
|
|
+
|
|
|
+在关注高亮区间之前,我们必须先看看路径(path)这个属性。路径(path)属性使用一个路径(path)元素来定义路径视图(PathView)内代理的滚动路径。路径使用startx与starty属性来链接路径(path)元素,例如PathLine,PathQuad和PathCubic。这些元素都使用二维数组来构造路径。
|
|
|
+
|
|
|
+当路径定义好之后,可以使用PathPercent和PathAttribute元素来进一步设置。它们被放置在路径元素之间,并且为经过它们的路径和代理提供更加细致的控制。PathPercent提供了如何控制每个元素之间覆盖区域部分的路径,然后反过来控制分布在这条路径上的代理元素,它们被按比例的分布播放。
|
|
|
+
|
|
|
+preferredHightlightBegin与preferredHighlightEnd属性由PathView(路径视图)输入到图片元素中。它们的值在0~1之间。结束值大于等于开始值。例如设置这些属性值为0.5,当前项只会显示当前百分之50的图像在这个路径上。
|
|
|
+
|
|
|
+在Path中,PathAttribute元素也是被放置在元素之间的,就像PathPercent元素。它们可以让你指定属性的值然后插入的路径中去。这些属性与代理绑定可以用来控制任意的属性。
|
|
|
+
|
|
|
+
|
|
|
+
|
|
|
+下面这个例子展示了路径视图(PathView)如何创建一个卡片视图,并且用户可以滑动它。我们使用了一些技巧来完成这个例子。路径由PathLine元素组成。使用PathPercent元素,它确保了中间的元素居中,并且给其它的元素提供了足够的空间。使用PathAttribute元素来控制旋转,大小和深度值(z-value)。
|
|
|
+
|
|
|
+在这个路径之上(path),需要设置路径视图(PathView)的pathItemCount属性。它控制了路径的浓密度。路径视图的路径(PathView.onPath)使用preferredHighlightBegin与preferredHighlightEnd来控制可见的代理项。
|
|
|
+
|
|
|
+```
|
|
|
+ PathView {
|
|
|
+ anchors.fill: parent
|
|
|
+
|
|
|
+ delegate: flipCardDelegate
|
|
|
+ model: 100
|
|
|
+
|
|
|
+ path: Path {
|
|
|
+ startX: root.width/2
|
|
|
+ startY: 0
|
|
|
+
|
|
|
+ PathAttribute { name: "itemZ"; value: 0 }
|
|
|
+ PathAttribute { name: "itemAngle"; value: -90.0; }
|
|
|
+ PathAttribute { name: "itemScale"; value: 0.5; }
|
|
|
+ PathLine { x: root.width/2; y: root.height*0.4; }
|
|
|
+ PathPercent { value: 0.48; }
|
|
|
+ PathLine { x: root.width/2; y: root.height*0.5; }
|
|
|
+ PathAttribute { name: "itemAngle"; value: 0.0; }
|
|
|
+ PathAttribute { name: "itemScale"; value: 1.0; }
|
|
|
+ PathAttribute { name: "itemZ"; value: 100 }
|
|
|
+ PathLine { x: root.width/2; y: root.height*0.6; }
|
|
|
+ PathPercent { value: 0.52; }
|
|
|
+ PathLine { x: root.width/2; y: root.height; }
|
|
|
+ PathAttribute { name: "itemAngle"; value: 90.0; }
|
|
|
+ PathAttribute { name: "itemScale"; value: 0.5; }
|
|
|
+ PathAttribute { name: "itemZ"; value: 0 }
|
|
|
+ }
|
|
|
+
|
|
|
+ pathItemCount: 16
|
|
|
+
|
|
|
+ preferredHighlightBegin: 0.5
|
|
|
+ preferredHighlightEnd: 0.5
|
|
|
+ }
|
|
|
+```
|
|
|
+
|
|
|
+
|
|
|
+代理如下面所示,使用了一些从PathAttribute中链接的属性,itemZ,itemAngle和itemScale。需要注意代理链接的属性只在wrapper中可用。因此,rotxs属性在Rotation元素中定义为可访问值。
|
|
|
+
|
|
|
+另一个需要注意的是路径视图(PathView)链接的PathView.onPath属性的用法。通常对于这个属性都绑定为可见,这样允许路径视图(PathView)缓冲不可见的元素。这不是通过剪裁处理来实现的,因为路径视图(PathView)的代理比其它的视图,例如链表视图(ListView)或者栅格视图(GridView)放置更加随意。
|
|
|
+
|
|
|
+```
|
|
|
+ Component {
|
|
|
+ id: flipCardDelegate
|
|
|
+
|
|
|
+ Item {
|
|
|
+ id: wrapper
|
|
|
+
|
|
|
+ width: 64
|
|
|
+ height: 64
|
|
|
+
|
|
|
+ visible: PathView.onPath
|
|
|
+
|
|
|
+ scale: PathView.itemScale
|
|
|
+ z: PathView.itemZ
|
|
|
+
|
|
|
+ property variant rotX: PathView.itemAngle
|
|
|
+ transform: Rotation { axis { x: 1; y: 0; z: 0 } angle: wrapper.rotX; origin { x: 32; y: 32; } }
|
|
|
+
|
|
|
+ Rectangle {
|
|
|
+ anchors.fill: parent
|
|
|
+ color: "lightGray"
|
|
|
+ border.color: "black"
|
|
|
+ border.width: 3
|
|
|
+ }
|
|
|
+
|
|
|
+ Text {
|
|
|
+ anchors.centerIn: parent
|
|
|
+ text: index
|
|
|
+ font.pixelSize: 30
|
|
|
+ }
|
|
|
+ }
|
|
|
+ }
|
|
|
+```
|
|
|
+
|
|
|
+当在路径视图(PathView)上使用图像转换或者其它更加复杂的元素时,有一个性能优化的技巧是绑定图像元素(Image)的smooth属性与PathView.view.moving属性。这意味着图像在移动时可能不够完美,但是能够比较平滑的转换。当视图在移动时,对于平滑缩放的处理是没有意义的,因为用户根本看不见这个过程。
|
|
|
+
|
|
|
+## 6.5.2 XML模型(A Model from XML)
|
|
|
+
|
|
|
+由于XML是一种常见的数据格式,QML提供了XmlListModel元素来包装XML数据。这个元素能够获取本地或者网络上的XML数据,然后通过XPath解析这些数据。
|
|
|
+
|
|
|
+下面这个例子展示了从RSS流中获取图片,源属性(source)引用了一个网络地址,这个数据会自动下载。
|
|
|
+
|
|
|
+
|
|
|
+
|
|
|
+当数据下载完成后,它会被加工作为模型的子项。查询属性(query)是一个XPath代理的基础查询,用来创建模型项。在这个例子中,这个路径是/rss/channel/item,所以,在一个模型子项创建后,每一个子项的标签,都包含了一个频道标签,包含一个RSS标签。
|
|
|
+
|
|
|
+每一个模型项,一些规则需要被提取,由XmlRole元素来代理。每一个规则都需要一个名称,这样代理才能够通过属性绑定来访问。每个这样的属性的值都通过XPath查询来确定。例如标题属性(title)符合title/string()查询,返回内容中在之间的值。
|
|
|
+
|
|
|
+图像源属性(imageSource)更加有趣,因为它不仅仅是从XML中提取字符串,也需要加载它。在流数据的支持下,每个子项包含了一个图片。使用XPath的函数substring-after与substring-before,可以提取本地的图片资源。这样imageSource属性就可以直接被作为一个Image元素的source属性使用。
|
|
|
+
|
|
|
+```
|
|
|
+import QtQuick 2.0
|
|
|
+import QtQuick.XmlListModel 2.0
|
|
|
+
|
|
|
+Item {
|
|
|
+ width: 300
|
|
|
+ height: 480
|
|
|
+
|
|
|
+ Component {
|
|
|
+ id: imageDelegate
|
|
|
+
|
|
|
+ Item {
|
|
|
+ width: listView.width
|
|
|
+ height: 400
|
|
|
+
|
|
|
+ Column {
|
|
|
+ Text {
|
|
|
+ text: title
|
|
|
+ }
|
|
|
+
|
|
|
+ Image {
|
|
|
+ source: imageSource
|
|
|
+ }
|
|
|
+ }
|
|
|
+ }
|
|
|
+ }
|
|
|
+
|
|
|
+ XmlListModel {
|
|
|
+ id: imageModel
|
|
|
+
|
|
|
+ source: "http://feeds.nationalgeographic.com/ng/photography/photo-of-the-day/"
|
|
|
+ query: "/rss/channel/item"
|
|
|
+
|
|
|
+ XmlRole { name: "title"; query: "title/string()" }
|
|
|
+ XmlRole { name: "imageSource"; query: "substring-before(substring-after(description/string(), 'img src=\"'), '\"')" }
|
|
|
+ }
|
|
|
+
|
|
|
+ ListView {
|
|
|
+ id: listView
|
|
|
+
|
|
|
+ anchors.fill: parent
|
|
|
+
|
|
|
+ model: imageModel
|
|
|
+ delegate: imageDelegate
|
|
|
+ }
|
|
|
+}
|
|
|
+```
|
|
|
+
|
|
|
+## 6.5.3 链表分段(Lists with Sections)
|
|
|
+
|
|
|
+有时,链表的数据需要划分段。例如使用首字母来划分联系人,或者音乐。使用链表视图可以把平面列表按类别划分。
|
|
|
+
|
|
|
+
|
|
|
+
|
|
|
+为了使用分段,section.property与section.criteria必须安装。section.property定义了哪些属性用于内容的划分。在这里,最重要的是知道每一段由哪些连续的元素构成,否则相同的属性名可能出现在几个不同的地方。
|
|
|
+
|
|
|
+section.criteria能够被设置为ViewSection.FullString或者ViewSection.FirstCharacter。默认下使用第一个值,能够被用于模型中有清晰的分段,例如音乐专辑。第二个是使用一个属性的首字母来分段,这说明任何属性都可以被使用。通常的例子是用于联系人名单的姓。
|
|
|
+
|
|
|
+当段被定义好后,每个子项能够使用绑定属性ListView.section,ListView.previousSection与ListView.nextSection来访问。使用这些属性,可以检测段的第一个与最后一个子项。
|
|
|
+
|
|
|
+使用链表视图(ListView)的section.delegate属性可以给段指定代理组件。它能够创建段标题,并且可以在任意子项之前插入这个段代理。使用绑定属性section可以访问当前段的名称。
|
|
|
+
|
|
|
+下面这个例子使用国际分类展示了分段的一些概念。国籍(nation)作为section.property,段代理组件(section.delegate)使用每个国家作为标题。在每个段中,spacemen模型中的名字使用spaceManDelegate组件来代理显示。
|
|
|
+
|
|
|
+```
|
|
|
+import QtQuick 2.0
|
|
|
+
|
|
|
+Rectangle {
|
|
|
+ width: 300
|
|
|
+ height: 290
|
|
|
+
|
|
|
+ color: "white"
|
|
|
+
|
|
|
+ ListView {
|
|
|
+ anchors.fill: parent
|
|
|
+ anchors.margins: 20
|
|
|
+
|
|
|
+ clip: true
|
|
|
+
|
|
|
+ model: spaceMen
|
|
|
+
|
|
|
+ delegate: spaceManDelegate
|
|
|
+
|
|
|
+ section.property: "nation"
|
|
|
+ section.delegate: sectionDelegate
|
|
|
+ }
|
|
|
+
|
|
|
+ Component {
|
|
|
+ id: spaceManDelegate
|
|
|
+
|
|
|
+ Item {
|
|
|
+ width: 260
|
|
|
+ height: 20
|
|
|
+
|
|
|
+ Text {
|
|
|
+ anchors.left: parent.left
|
|
|
+ anchors.verticalCenter: parent.verticalCenter
|
|
|
+ anchors.leftMargin: 10
|
|
|
+
|
|
|
+ font.pixelSize: 12
|
|
|
+
|
|
|
+ text: name
|
|
|
+ }
|
|
|
+ }
|
|
|
+ }
|
|
|
+
|
|
|
+ Component {
|
|
|
+ id: sectionDelegate
|
|
|
+
|
|
|
+ Rectangle {
|
|
|
+ width: 260
|
|
|
+ height: 20
|
|
|
+
|
|
|
+ color: "lightGray"
|
|
|
+
|
|
|
+ Text {
|
|
|
+ anchors.left: parent.left
|
|
|
+ anchors.verticalCenter: parent.verticalCenter
|
|
|
+ anchors.leftMargin: 10
|
|
|
+
|
|
|
+ font.pixelSize: 12
|
|
|
+ font.bold: true
|
|
|
+
|
|
|
+ text: section
|
|
|
+ }
|
|
|
+ }
|
|
|
+ }
|
|
|
+
|
|
|
+
|
|
|
+ ListModel {
|
|
|
+ id: spaceMen
|
|
|
+
|
|
|
+ ListElement { name: "Abdul Ahad Mohmand"; nation: "Afganistan"; }
|
|
|
+ ListElement { name: "Marcos Pontes"; nation: "Brazil"; }
|
|
|
+ ListElement { name: "Alexandar Panayotov Alexandrov"; nation: "Bulgaria"; }
|
|
|
+ ListElement { name: "Georgi Ivanov"; nation: "Bulgaria"; }
|
|
|
+ ListElement { name: "Roberta Bondar"; nation: "Canada"; }
|
|
|
+ ListElement { name: "Marc Garneau"; nation: "Canada"; }
|
|
|
+ ListElement { name: "Chris Hadfield"; nation: "Canada"; }
|
|
|
+ ListElement { name: "Guy Laliberte"; nation: "Canada"; }
|
|
|
+ ListElement { name: "Steven MacLean"; nation: "Canada"; }
|
|
|
+ ListElement { name: "Julie Payette"; nation: "Canada"; }
|
|
|
+ ListElement { name: "Robert Thirsk"; nation: "Canada"; }
|
|
|
+ ListElement { name: "Bjarni Tryggvason"; nation: "Canada"; }
|
|
|
+ ListElement { name: "Dafydd Williams"; nation: "Canada"; }
|
|
|
+ }
|
|
|
+}
|
|
|
+```
|
|
|
+
|
|
|
+## 6.5.4 性能协调(Tunning Performance)
|
|
|
+
|
|
|
+一个模型视图的性能很大程度上依赖于代理的创建。例如滚动下拉一个链表视图时,代理从外部加入到视图底部,并且从视图顶部移出。如果设置剪裁(clip)属性为false,并且代理项花了很多时间来初始化,用户会感觉到视图滚动体验很差。
|
|
|
+
|
|
|
+为了优化这个问题,你可以在滚动时使用像素来调整。使用cacheBuffer属性,在上诉情况下的垂直滚动,它将会调整在链表视图的上下需要预先准备好多少像素的代理项,结合异步加载图像元素(Image),例如在它们进入视图之前加载。
|
|
|
+
|
|
|
+创建更多的代理项将会牺牲一些流畅的体验,并且花更多的时间来初始化每个代理。这并不代表可以解决一些更加复杂的代理项的问题。在每次实例化代理时,它的内容都会被评估和编辑。这需要花费时间,如果它花费了太多的时间,它将会导致一个很差的滚动体验。在一个代理中包含太多的元素也会降低滚动的性能。
|
|
|
+
|
|
|
+为了补救这个问题,我们推荐使用动态加载元素。当它们需要时,可以初始化这些附加的元素。例如,一个展开代理可能推迟它的详细内容的实例化,直到需要使用它时。每个代理中最好减少JavaScript的数量。将每个代理中复杂的JavaScript调用放在外面来实现。这将会减少每个代理在创建时编译JavaScript。
|