首页 > 代码库 > qml demo分析(rssnews-常见新闻布局)

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

一、效果展示

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

技术分享

图1 新闻效果图

二、源码分析

  首先先来总体分析下该示例代码的工程目录,如图2所示,总共有6个qml文件。其中BusyIndicator和组件是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     onl oadingChanged: { 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=http://www.mamicode.com/.*/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: closeButton102         source: "content/images/btn_close.png"103         scale: 0.8104         anchors.top: parent.top105         anchors.right: parent.right106         anchors.margins: 4107         opacity: (isPortrait && categories.moving) ? 0.2 : 1.0108         Behavior on opacity {109             NumberAnimation { duration: 300; easing.type: Easing.OutSine }110         }111 112         MouseArea {113             anchors.fill: parent114             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 occurred10     function timeSinceEvent(pubDate) {11         var result = pubDate;12 13         // We need to modify the pubDate read from the RSS feed14         // so the JavaScript Date object can interpret it15         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                 else32                     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.width45         spacing: 846 47         Column {48             Item {//占位49                 width: 450                 height: titleText.font.pixelSize / 451             }52 53             Image {54                 id: titleImage55                 source: image//image对应模型中的字段56             }57         }58 59         Text {60             id: titleText61 62             text: title//image对应模型中的字段63             width: delegate.width - titleImage.width64             wrapMode: Text.WordWrap65             font.pixelSize: 2666             font.bold: true67         }68     }69 70     Text {//距离新闻发布时间+带有link字样的超链接71         width: delegate.width72         font.pixelSize: 1273         textFormat: Text.RichText74         font.italic: true75         text: timeSinceEvent(pubDate) + " (<a href=http://www.mamicode.com/"" + link + "\">Link</a>)"76         onLinkActivated: {77             Qt.openUrlExternally(link)//link对应模型中的字段78         }79     }80 81     Text {82         id: descriptionText83 84         text: description//对应模型中的字段85         width: parent.width86         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++中根本不可能出现。。。呵呵呵。。。

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