qml demo分析(rssnews-常见新闻布局)

一、效果展示

  今儿来分析一篇常见的ui布局,完全使用qml编写,ui交互效果友好,如图1所示,是一个常见的客户端新闻展示效果,左侧是一个列表,右侧是新闻详情。

图1 新闻效果图

二、源码分析

  首先先来总体分析下该示例代码的工程目录,如图2所示,总共有6个qml文件。其中BusyIndicator和ScrollBar组件是qml已经存在的组件,NewsDelegate组件是新闻详情页中的一项,CategoryDelegate组件是左侧列表中的一项,RssFeeds组件是左侧新闻列表数据源,rssnews文件是主程序文件。

图2 工程目录

  结合图1看程序工程目录,是不是觉着一目了然。NewsDelegate组件和CategoryDelegate组件是两个绘制代理,RssFeeds组件提供一个视图数据源,其中还有一个视图的数据源在rssnews.qml文件内部定义。接下来我主要分析下主程序文件rssnews.qml和NewsDelegate绘制代理

1、主程序文件

  主程序文件代码如下,程序中关键的地方都有注释,相比于之前的文章注释少了许多,大多都是一些常见的属性没有了注释。

  1 import QtQuick 2.2
  2 import QtQuick.XmlListModel 2.0
  3 import QtQuick.Window 2.1
  4 import "./content"
  5 
  6 Rectangle {
  7     id: window
  8 
  9     width: 800
 10     height: 480
 11 
 12     property string currentFeed: rssFeeds.get(0).feed//get方法为ListModel内置方法,返回指定索引item
 13     property bool loading: feedModel.status === XmlListModel.Loading//是否是加载中。。。
 14     property bool isPortrait: Screen.primaryOrientation === Qt.PortraitOrientation
 15 
 16     onLoadingChanged: {
 17         if (feedModel.status == XmlListModel.Ready)
 18             list.positionViewAtBeginning()
 19     }
 20 
 21     RssFeeds { id: rssFeeds }
 22 
 23     XmlListModel {
 24         id: feedModel
 25 
 26         source: "http://" + window.currentFeed
 27         query: "/rss/channel/item[child::media:content]"
 28         namespaceDeclarations: "declare namespace media = 'http://search.yahoo.com/mrss/';"
 29 
 30         XmlRole { name: "title"; query: "title/string()" }
 31         // Remove any links from the description
 32         XmlRole { name: "description"; query: "fn:replace(description/string(), '\<a href=.*\/a\>', '')" }
 33         XmlRole { name: "image"; query: "media:content/@url/string()" }
 34         XmlRole { name: "link"; query: "link/string()" }
 35         XmlRole { name: "pubDate"; query: "pubDate/string()" }
 36     }
 37 
 38     ListView {//左侧新闻列表
 39         id: categories
 40         property int itemWidth: 190
 41 
 42         width: isPortrait ? parent.width : itemWidth
 43         height: isPortrait ? itemWidth : parent.height
 44         orientation: isPortrait ? ListView.Horizontal : ListView.Vertical
 45         anchors.top: parent.top
 46         model: rssFeeds
 47         delegate: CategoryDelegate { itemSize: categories.itemWidth }
 48         spacing: 3
 49     }
 50 
 51     ScrollBar {
 52         id: listScrollBar
 53 
 54         orientation: isPortrait ? Qt.Horizontal : Qt.Vertical
 55         height: isPortrait ? 8 : categories.height;
 56         width: isPortrait ? categories.width : 8
 57         scrollArea: categories;//关联滚动的区域 
 58         anchors.right: categories.right//锚点定位
 59     }
 60 
 61     ListView {//右侧新闻详情  由多个项NewsDelegate组成
 62         id: list
 63 
 64         anchors.left: isPortrait ? window.left : categories.right
 65         anchors.right: closeButton.left
 66         anchors.top: isPortrait ? categories.bottom : window.top
 67         anchors.bottom: window.bottom
 68         anchors.leftMargin: 30
 69         anchors.rightMargin: 4
 70         clip: isPortrait
 71         model: feedModel
 72         footer: footerText//页脚  视图最底部的修饰
 73         delegate: NewsDelegate {}
 74     }
 75 
 76     ScrollBar {
 77         scrollArea: list
 78         width: 8
 79         anchors.right: window.right
 80         anchors.top: isPortrait ? categories.bottom : window.top
 81         anchors.bottom: window.bottom
 82     }
 83 
 84     Component {
 85         id: footerText
 86 
 87         Rectangle {
 88             width: parent.width
 89             height: closeButton.height
 90             color: "lightgray"
 91 
 92             Text {
 93                 text: "RSS Feed from Yahoo News"
 94                 anchors.centerIn: parent
 95                 font.pixelSize: 14
 96             }
 97         }
 98     }
 99 
100     Image {//关闭按钮  点击退出程序
101         id: closeButton
102         source: "content/images/btn_close.png"
103         scale: 0.8
104         anchors.top: parent.top
105         anchors.right: parent.right
106         anchors.margins: 4
107         opacity: (isPortrait && categories.moving) ? 0.2 : 1.0
108         Behavior on opacity {
109             NumberAnimation { duration: 300; easing.type: Easing.OutSine }
110         }
111 
112         MouseArea {
113             anchors.fill: parent
114             onClicked: {
115                 Qt.quit()
116             }
117         }
118     }
119 }

  footer属性指定页脚,就像word文件的页脚一样,位于ListView最低端,如图1中第一帧的RSS Feed from Yahoo News字段,其实每个页面都有这个字段,只是都位于ListView内容最低端。

2、新闻详情页中项

  如图1所示,该NewsDelegate绘制代理是右侧页面中的一条新闻项,右侧页面是一个ListView,内部有许多项组成,每一项都是由该代理绘制。

 1 //新闻详情中的一条
 2 import QtQuick 2.2
 3 
 4 Column {
 5     id: delegate
 6     width: delegate.ListView.view.width
 7     spacing: 8
 8 
 9     // Returns a string representing how long ago an event occurred
10     function timeSinceEvent(pubDate) {
11         var result = pubDate;
12 
13         // We need to modify the pubDate read from the RSS feed
14         // so the JavaScript Date object can interpret it
15         var d = pubDate.replace(',','').split(' ');
16         if (d.length != 6)
17             return result;
18 
19         var date = new Date([d[0], d[2], d[1], d[3], d[4], 'GMT' + d[5]].join(' '));
20 
21         if (!isNaN(date.getDate())) {
22             var age = new Date() - date;
23             var minutes = Math.floor(Number(age) / 60000);
24             if (minutes < 1440) {
25                 if (minutes < 2)
26                     result = qsTr("Just now");
27                 else if (minutes < 60)
28                     result = '' + minutes + ' ' + qsTr("minutes ago")
29                 else if (minutes < 120)
30                     result = qsTr("1 hour ago");
31                 else
32                     result = '' + Math.floor(minutes/60) + ' ' + qsTr("hours ago");
33             }
34             else {
35                 result = date.toDateString();
36             }
37         }
38         return result;
39     }
40 
41     Item { height: 8; width: delegate.width }
42 
43     Row {
44         width: parent.width
45         spacing: 8
46 
47         Column {
48             Item {//占位
49                 width: 4
50                 height: titleText.font.pixelSize / 4
51             }
52 
53             Image {
54                 id: titleImage
55                 source: image//image对应模型中的字段
56             }
57         }
58 
59         Text {
60             id: titleText
61 
62             text: title//image对应模型中的字段
63             width: delegate.width - titleImage.width
64             wrapMode: Text.WordWrap
65             font.pixelSize: 26
66             font.bold: true
67         }
68     }
69 
70     Text {//距离新闻发布时间+带有link字样的超链接
71         width: delegate.width
72         font.pixelSize: 12
73         textFormat: Text.RichText
74         font.italic: true
75         text: timeSinceEvent(pubDate) + " (<a href=\"" + link + "\">Link</a>)"
76         onLinkActivated: {
77             Qt.openUrlExternally(link)//link对应模型中的字段
78         }
79     }
80 
81     Text {
82         id: descriptionText
83 
84         text: description//对应模型中的字段
85         width: parent.width
86         wrapMode: Text.WordWrap//换行模式 字不能拆分
87         font.pixelSize: 14//字号
88         textFormat: Text.StyledText//支持一些基本的文本样式标记
89         horizontalAlignment: Qt.AlignLeft//水平靠左
90     }
91 }

三、小节

  看了有一些qml示例代码,也一直主要在分析qml代码,本小节插播一段个人总结吧,也算是小小感慨下。

  不同于以往的QWidget窗口程序,qml写界面非常简洁,从以往的示例中就能感觉的到,在友好交互方面qml比QWidget做的好,比如List下拉到头时的弹簧效果、完美的加载中展示和远程图片加载等等。qml是声明性语言,即不在像C++那样需要编译后才能运行,在代码编写时只需要关注ui,可以根据需要自己封装组件,把需要外界使用的属性使用导出的方式暴露给外界,每一个组件属性都有OnPropertyChanged槽,当属性发生变化时该槽随即执行。

  可能是由于一直从事C++相关的工作,没有声明性语言的基础,在阅读qml代码时总是感觉有一种代码散乱无处整理的感觉,现在小小的示例代码亦是如此,等到正真做起项目来不知道会是怎样一番场景。比如说绘制代理在访问一些属性时,直接访问的是模型中的字段,如果字段名称写错,这种错误只能到运行时异常后才能慢慢排查,类似于这样的代码其实有很多,突然之间就跳出一句无厘头的代码,这在C++中根本不可能出现。。。呵呵呵。。。

posted @ 2017-03-14 20:17  朝十晚八  阅读(666)  评论(0编辑  收藏  举报

返回顶部