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

时间:2023-03-09 07:15:20
qml demo分析(rssnews-常见新闻布局)

一、效果展示

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

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

图1 新闻效果图

二、源码分析

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

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

图2 工程目录

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

1、主程序文件

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

 import QtQuick 2.2
import QtQuick.XmlListModel 2.0
import QtQuick.Window 2.1
import "./content" Rectangle {
id: window width:
height: property string currentFeed: rssFeeds.get().feed//get方法为ListModel内置方法,返回指定索引item
property bool loading: feedModel.status === XmlListModel.Loading//是否是加载中。。。
property bool isPortrait: Screen.primaryOrientation === Qt.PortraitOrientation onLoadingChanged: {
if (feedModel.status == XmlListModel.Ready)
list.positionViewAtBeginning()
} RssFeeds { id: rssFeeds } XmlListModel {
id: feedModel source: "http://" + window.currentFeed
query: "/rss/channel/item[child::media:content]"
namespaceDeclarations: "declare namespace media = 'http://search.yahoo.com/mrss/';" XmlRole { name: "title"; query: "title/string()" }
// Remove any links from the description
XmlRole { name: "description"; query: "fn:replace(description/string(), '\<a href=.*\/a\>', '')" }
XmlRole { name: "image"; query: "media:content/@url/string()" }
XmlRole { name: "link"; query: "link/string()" }
XmlRole { name: "pubDate"; query: "pubDate/string()" }
} ListView {//左侧新闻列表
id: categories
property int itemWidth: width: isPortrait ? parent.width : itemWidth
height: isPortrait ? itemWidth : parent.height
orientation: isPortrait ? ListView.Horizontal : ListView.Vertical
anchors.top: parent.top
model: rssFeeds
delegate: CategoryDelegate { itemSize: categories.itemWidth }
spacing:
} ScrollBar {
id: listScrollBar orientation: isPortrait ? Qt.Horizontal : Qt.Vertical
height: isPortrait ? : categories.height;
width: isPortrait ? categories.width :
scrollArea: categories;//关联滚动的区域
anchors.right: categories.right//锚点定位
} ListView {//右侧新闻详情 由多个项NewsDelegate组成
id: list anchors.left: isPortrait ? window.left : categories.right
anchors.right: closeButton.left
anchors.top: isPortrait ? categories.bottom : window.top
anchors.bottom: window.bottom
anchors.leftMargin:
anchors.rightMargin:
clip: isPortrait
model: feedModel
footer: footerText//页脚 视图最底部的修饰
delegate: NewsDelegate {}
} ScrollBar {
scrollArea: list
width:
anchors.right: window.right
anchors.top: isPortrait ? categories.bottom : window.top
anchors.bottom: window.bottom
} Component {
id: footerText Rectangle {
width: parent.width
height: closeButton.height
color: "lightgray" Text {
text: "RSS Feed from Yahoo News"
anchors.centerIn: parent
font.pixelSize:
}
}
} Image {//关闭按钮 点击退出程序
id: closeButton
source: "content/images/btn_close.png"
scale: 0.8
anchors.top: parent.top
anchors.right: parent.right
anchors.margins:
opacity: (isPortrait && categories.moving) ? 0.2 : 1.0
Behavior on opacity {
NumberAnimation { duration: ; easing.type: Easing.OutSine }
} MouseArea {
anchors.fill: parent
onClicked: {
Qt.quit()
}
}
}
}

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

2、新闻详情页中项

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

 //新闻详情中的一条
import QtQuick 2.2 Column {
id: delegate
width: delegate.ListView.view.width
spacing: // Returns a string representing how long ago an event occurred
function timeSinceEvent(pubDate) {
var result = pubDate; // We need to modify the pubDate read from the RSS feed
// so the JavaScript Date object can interpret it
var d = pubDate.replace(',','').split(' ');
if (d.length != )
return result; var date = new Date([d[], d[], d[], d[], d[], 'GMT' + d[]].join(' ')); if (!isNaN(date.getDate())) {
var age = new Date() - date;
var minutes = Math.floor(Number(age) / );
if (minutes < ) {
if (minutes < )
result = qsTr("Just now");
else if (minutes < )
result = '' + minutes + ' ' + qsTr("minutes ago")
else if (minutes < )
result = qsTr("1 hour ago");
else
result = '' + Math.floor(minutes/) + ' ' + qsTr("hours ago");
}
else {
result = date.toDateString();
}
}
return result;
} Item { height: ; width: delegate.width } Row {
width: parent.width
spacing: Column {
Item {//占位
width:
height: titleText.font.pixelSize /
} Image {
id: titleImage
source: image//image对应模型中的字段
}
} Text {
id: titleText text: title//image对应模型中的字段
width: delegate.width - titleImage.width
wrapMode: Text.WordWrap
font.pixelSize:
font.bold: true
}
} Text {//距离新闻发布时间+带有link字样的超链接
width: delegate.width
font.pixelSize:
textFormat: Text.RichText
font.italic: true
text: timeSinceEvent(pubDate) + " (<a href=\"" + link + "\">Link</a>)"
onLinkActivated: {
Qt.openUrlExternally(link)//link对应模型中的字段
}
} Text {
id: descriptionText text: description//对应模型中的字段
width: parent.width
wrapMode: Text.WordWrap//换行模式 字不能拆分
font.pixelSize: //字号
textFormat: Text.StyledText//支持一些基本的文本样式标记
horizontalAlignment: Qt.AlignLeft//水平靠左
}
}

三、小节

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

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

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