高级用法(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元素。它们可以让你指定属性的值然后插入的路径中去。这些属性与代理绑定可以用来控制任意的属性。

高级用法(Advanced Techniques) - 图1

下面这个例子展示了路径视图(PathView)如何创建一个卡片视图,并且用户可以滑动它。我们使用了一些技巧来完成这个例子。路径由PathLine元素组成。使用PathPercent元素,它确保了中间的元素居中,并且给其它的元素提供了足够的空间。使用PathAttribute元素来控制旋转,大小和深度值(z-value)。

在这个路径之上(path),需要设置路径视图(PathView)的pathItemCount属性。它控制了路径的浓密度。路径视图的路径(PathView.onPath)使用preferredHighlightBegin与preferredHighlightEnd来控制可见的代理项。

  1. PathView {
  2. anchors.fill: parent
  3. delegate: flipCardDelegate
  4. model: 100
  5. path: Path {
  6. startX: root.width/2
  7. startY: 0
  8. PathAttribute { name: "itemZ"; value: 0 }
  9. PathAttribute { name: "itemAngle"; value: -90.0; }
  10. PathAttribute { name: "itemScale"; value: 0.5; }
  11. PathLine { x: root.width/2; y: root.height*0.4; }
  12. PathPercent { value: 0.48; }
  13. PathLine { x: root.width/2; y: root.height*0.5; }
  14. PathAttribute { name: "itemAngle"; value: 0.0; }
  15. PathAttribute { name: "itemScale"; value: 1.0; }
  16. PathAttribute { name: "itemZ"; value: 100 }
  17. PathLine { x: root.width/2; y: root.height*0.6; }
  18. PathPercent { value: 0.52; }
  19. PathLine { x: root.width/2; y: root.height; }
  20. PathAttribute { name: "itemAngle"; value: 90.0; }
  21. PathAttribute { name: "itemScale"; value: 0.5; }
  22. PathAttribute { name: "itemZ"; value: 0 }
  23. }
  24. pathItemCount: 16
  25. preferredHighlightBegin: 0.5
  26. preferredHighlightEnd: 0.5
  27. }

代理如下面所示,使用了一些从PathAttribute中链接的属性,itemZ,itemAngle和itemScale。需要注意代理链接的属性只在wrapper中可用。因此,rotxs属性在Rotation元素中定义为可访问值。

另一个需要注意的是路径视图(PathView)链接的PathView.onPath属性的用法。通常对于这个属性都绑定为可见,这样允许路径视图(PathView)缓冲不可见的元素。这不是通过剪裁处理来实现的,因为路径视图(PathView)的代理比其它的视图,例如链表视图(ListView)或者栅格视图(GridView)放置更加随意。

  1. Component {
  2. id: flipCardDelegate
  3. Item {
  4. id: wrapper
  5. width: 64
  6. height: 64
  7. visible: PathView.onPath
  8. scale: PathView.itemScale
  9. z: PathView.itemZ
  10. property variant rotX: PathView.itemAngle
  11. transform: Rotation { axis { x: 1; y: 0; z: 0 } angle: wrapper.rotX; origin { x: 32; y: 32; } }
  12. Rectangle {
  13. anchors.fill: parent
  14. color: "lightGray"
  15. border.color: "black"
  16. border.width: 3
  17. }
  18. Text {
  19. anchors.centerIn: parent
  20. text: index
  21. font.pixelSize: 30
  22. }
  23. }
  24. }

当在路径视图(PathView)上使用图像转换或者其它更加复杂的元素时,有一个性能优化的技巧是绑定图像元素(Image)的smooth属性与PathView.view.moving属性。这意味着图像在移动时可能不够完美,但是能够比较平滑的转换。当视图在移动时,对于平滑缩放的处理是没有意义的,因为用户根本看不见这个过程。

6.5.2 XML模型(A Model from XML)

由于XML是一种常见的数据格式,QML提供了XmlListModel元素来包装XML数据。这个元素能够获取本地或者网络上的XML数据,然后通过XPath解析这些数据。

下面这个例子展示了从RSS流中获取图片,源属性(source)引用了一个网络地址,这个数据会自动下载。

高级用法(Advanced Techniques) - 图2

当数据下载完成后,它会被加工作为模型的子项。查询属性(query)是一个XPath代理的基础查询,用来创建模型项。在这个例子中,这个路径是/rss/channel/item,所以,在一个模型子项创建后,每一个子项的标签,都包含了一个频道标签,包含一个RSS标签。

每一个模型项,一些规则需要被提取,由XmlRole元素来代理。每一个规则都需要一个名称,这样代理才能够通过属性绑定来访问。每个这样的属性的值都通过XPath查询来确定。例如标题属性(title)符合title/string()查询,返回内容中在之间的值。

图像源属性(imageSource)更加有趣,因为它不仅仅是从XML中提取字符串,也需要加载它。在流数据的支持下,每个子项包含了一个图片。使用XPath的函数substring-after与substring-before,可以提取本地的图片资源。这样imageSource属性就可以直接被作为一个Image元素的source属性使用。

  1. import QtQuick 2.0
  2. import QtQuick.XmlListModel 2.0
  3. Item {
  4. width: 300
  5. height: 480
  6. Component {
  7. id: imageDelegate
  8. Item {
  9. width: listView.width
  10. height: 400
  11. Column {
  12. Text {
  13. text: title
  14. }
  15. Image {
  16. source: imageSource
  17. }
  18. }
  19. }
  20. }
  21. XmlListModel {
  22. id: imageModel
  23. source: "http://feeds.nationalgeographic.com/ng/photography/photo-of-the-day/"
  24. query: "/rss/channel/item"
  25. XmlRole { name: "title"; query: "title/string()" }
  26. XmlRole { name: "imageSource"; query: "substring-before(substring-after(description/string(), 'img src=\"'), '\"')" }
  27. }
  28. ListView {
  29. id: listView
  30. anchors.fill: parent
  31. model: imageModel
  32. delegate: imageDelegate
  33. }
  34. }

6.5.3 链表分段(Lists with Sections)

有时,链表的数据需要划分段。例如使用首字母来划分联系人,或者音乐。使用链表视图可以把平面列表按类别划分。

高级用法(Advanced Techniques) - 图3

为了使用分段,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组件来代理显示。

  1. import QtQuick 2.0
  2. Rectangle {
  3. width: 300
  4. height: 290
  5. color: "white"
  6. ListView {
  7. anchors.fill: parent
  8. anchors.margins: 20
  9. clip: true
  10. model: spaceMen
  11. delegate: spaceManDelegate
  12. section.property: "nation"
  13. section.delegate: sectionDelegate
  14. }
  15. Component {
  16. id: spaceManDelegate
  17. Item {
  18. width: 260
  19. height: 20
  20. Text {
  21. anchors.left: parent.left
  22. anchors.verticalCenter: parent.verticalCenter
  23. anchors.leftMargin: 10
  24. font.pixelSize: 12
  25. text: name
  26. }
  27. }
  28. }
  29. Component {
  30. id: sectionDelegate
  31. Rectangle {
  32. width: 260
  33. height: 20
  34. color: "lightGray"
  35. Text {
  36. anchors.left: parent.left
  37. anchors.verticalCenter: parent.verticalCenter
  38. anchors.leftMargin: 10
  39. font.pixelSize: 12
  40. font.bold: true
  41. text: section
  42. }
  43. }
  44. }
  45. ListModel {
  46. id: spaceMen
  47. ListElement { name: "Abdul Ahad Mohmand"; nation: "Afganistan"; }
  48. ListElement { name: "Marcos Pontes"; nation: "Brazil"; }
  49. ListElement { name: "Alexandar Panayotov Alexandrov"; nation: "Bulgaria"; }
  50. ListElement { name: "Georgi Ivanov"; nation: "Bulgaria"; }
  51. ListElement { name: "Roberta Bondar"; nation: "Canada"; }
  52. ListElement { name: "Marc Garneau"; nation: "Canada"; }
  53. ListElement { name: "Chris Hadfield"; nation: "Canada"; }
  54. ListElement { name: "Guy Laliberte"; nation: "Canada"; }
  55. ListElement { name: "Steven MacLean"; nation: "Canada"; }
  56. ListElement { name: "Julie Payette"; nation: "Canada"; }
  57. ListElement { name: "Robert Thirsk"; nation: "Canada"; }
  58. ListElement { name: "Bjarni Tryggvason"; nation: "Canada"; }
  59. ListElement { name: "Dafydd Williams"; nation: "Canada"; }
  60. }
  61. }

6.5.4 性能协调(Tunning Performance)

一个模型视图的性能很大程度上依赖于代理的创建。例如滚动下拉一个链表视图时,代理从外部加入到视图底部,并且从视图顶部移出。如果设置剪裁(clip)属性为false,并且代理项花了很多时间来初始化,用户会感觉到视图滚动体验很差。

为了优化这个问题,你可以在滚动时使用像素来调整。使用cacheBuffer属性,在上诉情况下的垂直滚动,它将会调整在链表视图的上下需要预先准备好多少像素的代理项,结合异步加载图像元素(Image),例如在它们进入视图之前加载。

创建更多的代理项将会牺牲一些流畅的体验,并且花更多的时间来初始化每个代理。这并不代表可以解决一些更加复杂的代理项的问题。在每次实例化代理时,它的内容都会被评估和编辑。这需要花费时间,如果它花费了太多的时间,它将会导致一个很差的滚动体验。在一个代理中包含太多的元素也会降低滚动的性能。

为了补救这个问题,我们推荐使用动态加载元素。当它们需要时,可以初始化这些附加的元素。例如,一个展开代理可能推迟它的详细内容的实例化,直到需要使用它时。每个代理中最好减少JavaScript的数量。将每个代理中复杂的JavaScript调用放在外面来实现。这将会减少每个代理在创建时编译JavaScript。