PathView

PathView是 QtQuick 中最强大的视图,同时也是最复杂的。PathView允许创建一种更灵活的视图。在这种视图中,数据项并不是方方正正,而是可以沿着任意路径布局。沿着同一布局路径,数据项的属性可以被更详细的设置,例如缩放、透明度等。

使用PathView首先需要定义一个代理和一个路径。除此之外,PathView还可以设置很多其它属性,其中最普遍的是pathItemCount,用于设置可视数据项的数目;preferredHighlightBeginpreferredHighlightEndhighlightRangeMode可以设置高亮的范围,也就是沿着路径上面的当前可以被显示的数据项。

在深入了解高亮范围之前,我们必须首先了解path属性。path接受一个Path元素,用于定义PathView中的代理所需要的路径。该路径使用startXstartY属性,结合PathLinePathQuadPathCubic等路径元素进行定义。这些元素可以结合起来形成一个二维路径。

一旦路径定义完成,我们可以使用PathPercentPathAttribute元素进行调整。这些元素用于两个路径元素之间,更好的控制路径和路径上面的代理。PathPercent控制两个元素之间的路径部分有多大。它控制了路径上面代理的分布,这些代理按照其定义的百分比进行分布。

PathAttribute元素同PathPercent同样放置在元素之间。该元素允许沿路径插入一些属性值。这些属性值附加到代理上面,可用于任何能够使用的属性。

下面的例子演示了如何利用PathView实现卡片的弹入。这里使用了一些技巧来达到这一目的。它的路径包含三个PathLine元素。通过PathPercent元素,中间的元素可以正好位于中央位置,并且能够留有充足的空间,以避免被别的元素遮挡。元素的旋转、大小缩放和 Z 轴都是由PathAttribute进行控制。除了定义路径,我们还设置了PathViewpathItemCount属性。该属性用于指定路径所期望的元素个数。最后,代理中的PathView.onPath使用preferredHighlightBeginpreferredHighlightEnd属性控制代理的可见性。

 
 
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
    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元素附加的itemZitemAngleitemScale属性。需要注意的是,被附加到代理的属性只能在wrapper中使用。因此,我们又定义了一个rotX属性,以便在内部的Rotation元素中使用。另一点需要注意的是附件属性PathView.onPath的使用。通常我们会将这个属性绑定到可视化属性,这样允许PathView保留非可见元素,以便进行缓存。如果不这样设置,不可见元素可能会由于界面裁剪等原因被销毁,因为PathViewListViewGridView要灵活得多,所以为提高性能,一般会使用这种绑定实现缓存。

 
 
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
    Component {
        id: flipCardDelegate
 
        BlueBox {
            id: wrapper
 
            width: 64
            height: 64
            antialiasing: true
 
            gradient: Gradient {
                GradientStop { position: 0.0; color: "#2ed5fa" }
                GradientStop { position: 1.0; color: "#2467ec" }
            }
 
            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; }
            }
            text: index
        }
    }

示例运行结果如下:

完成PathView中图片和一些复杂元素的变换之后,通常会进行一定的性能优化,比如,将Image元素的smooth属性绑定到PathView.view.moving附加属性。这意味着在移动时,图片质量会稍有下降,静止时恢复正常。在视图移动时,很少有用户会在意图片的清晰度,因此,这样的妥协一般是可以接受的。

从 XML 加载模型

XML 是一种非常常见的数据格式,QML 提供了XmlListModel元素支持将 XML 数据转换为模型。XmlListModel可以加载本地或远程的 XML 文档,使用 XPath 表达式处理数据。

下面的例子给出了如何从 RSS 获取图片。source属性指向了一个远程地址,其数据会被自动下载下来。

 
 
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
import QtQuick 2.0
import QtQuick.XmlListModel 2.0
 
Background {
    width: 300
    height: 480
 
    Component {
        id: imageDelegate
 
        Box {
            width: listView.width
            height: 220
            color: '#333'
 
            Column {
                Text {
                    text: title
                    color: '#e0e0e0'
                }
                Image {
                    width: listView.width
                    height: 200
                    fillMode: Image.PreserveAspectCrop
                    source: imageSource
                }
            }
        }
    }
 
    XmlListModel {
        id: imageModel
 
        source: "http://www.padmag.cn/feed"
        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
    }
}

当数据被下载下来,这个 XML 就被处理成模型的数据项和角色。query属性是 XPath 表达式语言,用于创建模型数据项。在这个例子中,该属性值为/rss/channel/item,因此,rss 标签下的每一个 channel 标签中的每一个 item 标签,都会被生成一个数据项。每一个数据项都可以定义一系列角色,这些角色使用XmlRole表示。每一个角色都有一个名字,代理可以使用附件属性访问到其值。角色的值是使用 XPath 表达式获取的。例如,title属性的值由title/string()表达式决定,返回的是<title></title>标签之间的文本。imageSource属性值则更有趣。它并不是直接由 XML 获取的字符串,而是一系列函数的运算结果。在返回的 XML 中,有些 item 中包含图片,使用<img src=标签表示。使用substring-aftersubstring-beforeXPath 函数,可以找到每张图片的地址并返回。因此,imageSource属性可以直接作为Image元素的source属性值。

分组列表

有时,列表中的数据可以分成几个部分,例如,按照列表数据的首字母分组。利用ListView可以将一个扁平的列表分为几个组,如下图所示:

为了使用分组,需要设置section.propertysection.criteria两个属性。section.property定义了使用哪个属性进行分组。这里,需要确保模型已经排好序了,以便每一部分能够包含连续的元素,否则,同一属性的名字可能出现在多个位置。section.criteria的可选值为ViewSection.FullStringViewSection.FirstCharacter。前者为默认值,适用于具有明显分组的模型,例如,音乐集等;后者按照属性首字母分组,并且意味着所有属性都适用于此,常见例子是电话本的通讯录名单。

一旦分组定义完毕,在每一个数据项就可以使用附加属性ListView.sectionListView.previousSectionListView.nextSection访问到这个分组。使用这个属性,我们就可以找到一个分组的第一个和最后一个元素,从而实现某些特殊功能。

我们也可以给ListViewsection.delegate属性赋值,以便自定义分组显示的代理。这会在一个组的数据项之前插入一个用于显示分组的代理。这个代理可以使用附加属性访问当前分组的名字。

下面的例子按照国别对一组人进行分组。国别被设置为section.property属性的值。section.delegate组件,也就是sectionDelegate,用于显示每组的名字,也就是国家名。每组中的人名则使用spaceManDelegate显示。

 
 
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
import QtQuick 2.0
 
Background {
    width: 300
    height: 290
 
    ListView {
        anchors.fill: parent
        anchors.margins: 20
 
        clip: true
 
        model: spaceMen
 
        delegate: spaceManDelegate
 
        section.property: "nation"
        section.delegate: sectionDelegate
    }
 
    Component {
        id: spaceManDelegate
 
        Item {
            width: ListView.view.width
            height: 20
            Text {
                anchors.left: parent.left
                anchors.verticalCenter: parent.verticalCenter
                anchors.leftMargin: 8
                font.pixelSize: 12
                text: name
                color: '#1f1f1f'
            }
        }
    }
 
    Component {
        id: sectionDelegate
 
        BlueBox {
            width: ListView.view.width
            height: 20
            text: section
            fontColor: '#e0e0e0'
        }
    }
 
 
    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"; }
    }
}

关于性能

模型视图的性能很大程度上取决于创建新的代理所造成的消耗。例如,如果clip属性设置为false,当向下滚动ListView时,系统会在列表末尾创建新的代理,并且将列表上部已经不可显示的代理移除。显然,当初始化代理需要消耗大量时间时,用户在快速拖动滚动条时,这种现象就会造成一定程度的影响。

为了避免这种情况,你可以调整被滚动视图的外边框的值。通过修改cacheBuffer属性即可达到这一目的。在上面所述的有关竖直滚动的例子中,这个属性会影响到列表上方和下方会有多少像素。这些像素则影响到是否能够容纳这些代理。例如,将异步加载图片与此结合,就可以实现在图片真正加载完毕之后才显示出来。

更多的代理意味着更多的内存消耗,从而影响到用户的操作流畅度,同时也有关代理初始化的时间。对于复杂的代理,上面的方法并不能从根本上解决问题。代理初始化一次,其内容就会被重新计算。这会消耗时间,如果这个时间很长,很显然,这会降低用户体验。代理中子元素的个数同样也有影响。原因很简单,移动更多的元素当然要更多的时间。为了解决前面所说的两个问题,我们推荐使用Loader元素。Loader元素允许延时加载额外的元素。例如,一个可展开的代理,只有当用户点击时,才会显示这一项的详细信息,包含一个很大的图片。那么,利用Loader元素,我们可以做到,只有其被显示时才进行加载,否则不加载。基于同样的原因,应该使每个代理中包含的 JavaScript 代码尽可能少。最好能做到在代理之外调用复杂的 JavaScript 代码。这将减少代理创建时编译 JavaScript 所消耗的时间。

Qt 学习之路:模型-视图高级技术的更多相关文章

  1. Qt 学习之路:视图选择 (QItemSelectionModel)

    选择是视图中常用的一个操作.在列表.树或者表格中,通过鼠标点击可以选中某一项,被选中项会变成高亮或者反色.在 Qt 中,选择也是使用了一种模型.在 model/view 架构中,这种选择模型提供了一种 ...

  2. Qt 学习之路 :视图代理

    与 Qt model/view 架构类似,在自定义用户界面中,代理扮演着重要的角色.模型中的每一个数据项都要通过一个代理向用户展示,事实上,用户看到的可视部分就是代理. 每一个代理都可以访问一系列属性 ...

  3. 《Qt 学习之路 2》目录

    <Qt 学习之路 2>目录 <Qt 学习之路 2>目录  豆子  2012年8月23日  Qt 学习之路 2  177条评论 <Qt 学习之路 2>目录 序 Qt ...

  4. Qt 学习之路 2(51):布尔表达式树模型

    Qt 学习之路 2(51):布尔表达式树模型 豆子 2013年5月15日 Qt 学习之路 2 17条评论 本章将会是自定义模型的最后一部分.原本打算结束这部分内容,不过实在不忍心放弃这个示例.来自于 ...

  5. Qt 学习之路 2(46):视图和委托

    Home / Qt 学习之路 2 / Qt 学习之路 2(46):视图和委托 Qt 学习之路 2(46):视图和委托  豆子  2013年3月11日  Qt 学习之路 2  63条评论 前面我们介绍了 ...

  6. Qt 学习之路 2(56):使用模型操作数据库

    Qt 学习之路 2(56):使用模型操作数据库 (okgogo: skip) 豆子 2013年6月20日 Qt 学习之路 2 13条评论 前一章我们使用 SQL 语句完成了对数据库的常规操作,包括简单 ...

  7. Qt 学习之路 2(50):自定义可编辑模型

    Home / Qt 学习之路 2 / Qt 学习之路 2(50):自定义可编辑模型 Qt 学习之路 2(50):自定义可编辑模型 豆子 2013年5月13日 Qt 学习之路 2 13条评论 上一章我们 ...

  8. Qt 学习之路 2(49):自定义只读模型

    Qt 学习之路 2(49):自定义只读模型 豆子 2013年5月5日 Qt 学习之路 2 18条评论 model/view 模型将数据与视图分割开来,也就是说,我们可以为不同的视图,QListView ...

  9. Qt 学习之路 2(47):视图选择

    Qt 学习之路 2(47):视图选择 豆子 2013年3月28日 Qt 学习之路 2 34条评论 选择是视图中常用的一个操作.在列表.树或者表格中,通过鼠标点击可以选中某一项,被选中项会变成高亮或者反 ...

随机推荐

  1. Android 下拉刷新控件Android-PullToRefresh

    需要用到一个开源库 Android-PullToRefresh https://github.com/chrisbanes/Android-PullToRefresh ---------------- ...

  2. 2016030205 - ubuntu安装mysql

    ubuntu上安装mysql 1.检查ubuntu上是否已经安装mysql sudo netstat -tap | grep mysql 本机上没有安装mysql 2.安装mysql服务器端和客户端 ...

  3. AlertDialog中EditText不能获取焦点以及不宽度不能自动铺满的完美解决方案

    问题分析: 因为 dialog的Attributes使用的默认的,其中一个属性就是:flags ,就是这个属性导致不能获取焦点,默认的是FLAG_NOT_FOCUSABLE,故名思义不能获取输入焦点, ...

  4. 调用相册怎么设置剪裁-b

    //创建一个相册控制器 UIImagePickerController *pc = [[UIImagePickerController alloc] init]; //图片来源// UIImagePi ...

  5. PC硬件之我见——CPU篇

    写在最前面:     最近身边很多朋友都购置电脑的想法,往往也会选择性价比较高的DIY攒机方式.不幸的是,并不是所有人都对电脑硬件有一定的了解的,何况在现在这种社会风气下,盲目的相信商家是不明智的.所 ...

  6. 精心挑选的12款优秀 jQuery Ajax 分页插件和教程

    在这篇文章中,我为大家收集了12个基于 jQuery 框架的 Ajax 分页插件,这些插件都提供了详细的使用教程和演示.Ajax 技术的出现使得 Web 项目的用户体验有了极大的提高,如今借助优秀的  ...

  7. E: Write error - write (28 No space left on device)

    1:在终端中运行cd命令,提示: e: Write error - write (28 No space left on device) E: Cant mmap an empty file 2:使用 ...

  8. uva 1428 - Ping pong

    树状数组,把他们的技能值作为轴: 首先按照编号从小到大插入值,这样就可以得到,技能值比当前小的人数: 然后按照编号从大到小再插一遍: 代码: #include<cstdio> #inclu ...

  9. c#回调函数写法

    添加一个cs文件,在里面定义回调 using System; using System.Collections.Generic; using System.Linq; using System.Web ...

  10. 有感,懂市场比懂产品重要,懂产品比懂技术重要——想起凡客诚品和YY语音了

    一个创业公司,最好三样都要有,但应该CEO是懂市场,经理懂产品,程序员最好懂技术厉害一点-这还不算,销售也要厉害一点,不能守株待兔- 美工——有钱最好请个美工,最起码也要请人设计修改一下- 财务——不 ...