创建可以重复利用的QML Component--Podcast播放器代码实例
2015-09-30 13:05
357 查看
在我们设计QML应用时,很重要的一点就是设计一个可以被重复利用的软件Component.它可以反复在其它的应用中被利用.这如同在我们的C++及其它语言中设计自己的应用一样,我们可以创建自己的模块.只需要修改很少的部分或甚至不用做任何的修改就可以被其它的应用广泛使用.我们在前两天的文章"利用Ubuntu Component
Store来增加我们的QML Components"中已经做过一些介绍了.今天我们通过一个具体的实例来展示是如何做的.在今天的例程中,我们来介绍一个podcast播放器的实例.
在我们今天设计的应用中,有两个页面.第一个页面,是一个podcast RSS feed的介绍及其播放audio的列表.在第二个页面中,显示的是它的一个logo的图像及几个可以用于播放的按钮.
这个设计采用了一个叫做GenericPodcastApp.qml的Component.其设计如下:
在上面的设计中非常简单.我们通过在Component中设计一些property,在使用这个Component时只需要在外面对他们进行赋值即可.就像我以前在有些教程中讲到的.如果我们需要修改到这个Component中的其中的子Component的属性的话,我们可以通过QML中的alias来实现.第一个页面的最上面显示的是一个podcast的最基本的信息.在它的最下面,它使用了一个repeater来显示所有的epsode列表.
在这里我们使用了rssmodel,它的定义如下:
更多关于XmlListModel的使用,可以参阅API介绍.在我的博客中也有很多的介绍.大家可以参阅我的一些设计的例程.
到目前为止,我们已经设计好我的GenericPodcastApp Component.如果在其它的地方需要应用它的话,我们只需要这么做:
它的应用非常地简单.我们只需要对它里面的properties进行赋值即可.我们很容地把它修个成为其它的源的podcast.当然我们也可以利用这个设计更加复杂的应用,这样,我们可以把我们想要的rss feed轻易地加入到我们的podcast列表中.这个练习就交给我们的开发者吧.
整个项目的源码在:https://github.com/liu-xiao-guo/podcast
Store来增加我们的QML Components"中已经做过一些介绍了.今天我们通过一个具体的实例来展示是如何做的.在今天的例程中,我们来介绍一个podcast播放器的实例.
在我们今天设计的应用中,有两个页面.第一个页面,是一个podcast RSS feed的介绍及其播放audio的列表.在第二个页面中,显示的是它的一个logo的图像及几个可以用于播放的按钮.
这个设计采用了一个叫做GenericPodcastApp.qml的Component.其设计如下:
GenericPodcastApp.qml
import QtQuick 2.0 import Ubuntu.Components 0.1 import QtQuick.XmlListModel 2.0 import Ubuntu.Components.ListItems 0.1 as ListItem import QtMultimedia 5.0 PageStack { id: ps Component.onCompleted: ps.push(front) property alias squareLogo: logo.source property alias author: author.text property alias category: category.text property alias name: front.title property alias description: desc.text property alias feed: rssmodel.source Action { id: reloadAction text: "Reload" iconName: "reload" onTriggered: rssmodel.reload() } Page { id: front visible: true tools: ToolbarItems { ToolbarButton { action: reloadAction } } Flickable { anchors.fill: parent contentHeight: row.height + desc.height + showlist.height + desc.anchors.topMargin + showlist.anchors.topMargin Row { id: row width: parent.width anchors.top: parent.top anchors.left: parent.left anchors.topMargin: units.gu(1) anchors.leftMargin: units.gu(1) anchors.rightMargin: units.gu(1) spacing: units.gu(2) UbuntuShape { id: logoshape width: parent.width / 3 height: parent.width / 3 image: Image { id: logo fillMode: Image.PreserveAspectFit } ActivityIndicator { running: logo.status != Image.Ready anchors.centerIn: logoshape } } Column { width: row.width - row.spacing - row.anchors.leftMargin- row.anchors.rightMargin - logoshape.width spacing: units.gu(1) anchors.bottom: parent.bottom Label { id: author fontSize: "small" wrapMode: Text.WordWrap width: parent.width } Label { id: category wrapMode: Text.WordWrap width: parent.width fontSize: "small" } } } Label { id: desc anchors.top: row.bottom anchors.left: parent.left anchors.topMargin: units.gu(2) anchors.leftMargin: row.anchors.leftMargin width: parent.width - (row.anchors.leftMargin * 2) wrapMode: Text.WrapAtWordBoundaryOrAnywhere property bool expanded: false clip: true height: { if (desc.contentHeight > units.gu(12) && !expanded) { return units.gu(12) } return desc.contentHeight } Rectangle { color: "black" width: moretxt.contentWidth + units.gu(2) height: moretxt.contentHeight anchors.bottom: desc.bottom anchors.right: desc.right Label { id: moretxt color: "white" anchors.centerIn: parent text: desc.expanded ? "<<" : ">>" } visible: desc.contentHeight > units.gu(12) } MouseArea { anchors.fill: parent onClicked: desc.expanded = !desc.expanded } } Column { id: showlist anchors.top: desc.bottom anchors.topMargin: units.gu(2) width: parent.width Repeater { model: rssmodel ListItem.Standard { text: title width: parent.width progression: true onClicked: { ps.push(episode, {download: model.download, summary: model.summary, title: model.title}); } } } } ActivityIndicator { anchors.top: desc.bottom anchors.topMargin: units.gu(2) height: reloadbutton.height width: height anchors.horizontalCenter: parent.horizontalCenter running: rssmodel.status != XmlListModel.Ready && rssmodel.status != XmlListModel.Error } } } Page { id: episode property string download property string summary visible: false Flickable { anchors.fill: parent contentHeight: biglogo.height + positionbar.height + buttons.height + epdesc.height + (epcol.spacing * 4) Column { id: epcol width: parent.width spacing: units.gu(2) Image { id: biglogo source: logo.source width: parent.width height: parent.width fillMode: Image.PreserveAspectFit } Rectangle { id: positionbar width: buttons.width anchors.horizontalCenter: parent.horizontalCenter height: units.gu(5) color: "transparent" Rectangle { id: actualbar width: parent.width height: units.gu(0.5) color: "#999999" anchors.verticalCenter: parent.verticalCenter anchors.centerIn: parent } Rectangle { width: units.gu(0.5) height: units.gu(2) color: "#444444" anchors.verticalCenter: actualbar.verticalCenter x: actualbar.width * aud.position / aud.duration } MouseArea { anchors.fill: parent onPressed: { aud.seek(aud.duration * mouse.x / actualbar.width) } } } Row { id: buttons spacing: units.gu(2) anchors.horizontalCenter: parent.horizontalCenter Button { text: "<<30" onClicked: aud.seek(aud.position - 30000) } Button { text: aud.status == Audio.Loading ? "load" : (aud.playbackState == Audio.PlayingState ? "Stop" : "Play") onClicked: { aud.source = episode.download; if (aud.playbackState == Audio.PlayingState) { aud.pause(); } else { aud.play(); } console.log(aud.duration, aud.position); } } Button { text: "30>>" onClicked: aud.seek(aud.position + 30000) } } Label { id: epdesc width: parent.width - units.gu(4) anchors.horizontalCenter: parent.horizontalCenter text: episode.summary wrapMode: Text.Wrap color: "white" textFormat: Text.RichText } } } } XmlListModel { id: rssmodel query: "/rss/channel/item" namespaceDeclarations: "declare namespace itunes='http://www.itunes.com/dtds/podcast-1.0.dtd'; declare namespace content='http://purl.org/rss/1.0/modules/content/';" XmlRole { name: "title"; query: "title/string()" } XmlRole { name: "pubDate"; query: "pubDate/string()" } XmlRole { name: "download"; query: "enclosure/@url/string()" } XmlRole { name: "summary"; query: "content:encoded/string()" } } Audio { id: aud } }
在上面的设计中非常简单.我们通过在Component中设计一些property,在使用这个Component时只需要在外面对他们进行赋值即可.就像我以前在有些教程中讲到的.如果我们需要修改到这个Component中的其中的子Component的属性的话,我们可以通过QML中的alias来实现.第一个页面的最上面显示的是一个podcast的最基本的信息.在它的最下面,它使用了一个repeater来显示所有的epsode列表.
Column { id: showlist anchors.top: desc.bottom anchors.topMargin: units.gu(2) width: parent.width Repeater { model: rssmodel ListItem.Standard { text: title width: parent.width progression: true onClicked: { ps.push(episode, {download: model.download, summary: model.summary, title: model.title}); } } } }
在这里我们使用了rssmodel,它的定义如下:
XmlListModel { id: rssmodel query: "/rss/channel/item" namespaceDeclarations: "declare namespace itunes='http://www.itunes.com/dtds/podcast-1.0.dtd'; declare namespace content='http://purl.org/rss/1.0/modules/content/';" XmlRole { name: "title"; query: "title/string()" } XmlRole { name: "pubDate"; query: "pubDate/string()" } XmlRole { name: "download"; query: "enclosure/@url/string()" } XmlRole { name: "summary"; query: "content:encoded/string()" } }
更多关于XmlListModel的使用,可以参阅API介绍.在我的博客中也有很多的介绍.大家可以参阅我的一些设计的例程.
到目前为止,我们已经设计好我的GenericPodcastApp Component.如果在其它的地方需要应用它的话,我们只需要这么做:
Main.qml
import QtQuick 2.0 import Ubuntu.Components 1.1 /*! \brief MainView with a Label and Button elements. */ MainView { // objectName for functional testing purposes (autopilot-qt5) objectName: "mainView" // Note! applicationName needs to match the "name" field of the click manifest applicationName: "podcast.liu-xiao-guo" /* This property enables the application to change orientation when the device is rotated. The default is false. */ //automaticOrientation: true // Removes the old toolbar and enables new features of the new header. useDeprecatedToolbar: false width: units.gu(60) height: units.gu(85) GenericPodcastApp { name: "Bad Voltage" squareLogo: "images/logo.jpg" author: "Stuart Langridge, Jono Bacon, Jeremy Garcia, and Bryan Lunduke" category: "Technology" feed: "http://www.badvoltage.org/feed/ogg/" description: "Every two weeks Bad Voltage delivers an amusing take on technology, Open Source, politics, music, and anything else we think is interesting." } }
它的应用非常地简单.我们只需要对它里面的properties进行赋值即可.我们很容地把它修个成为其它的源的podcast.当然我们也可以利用这个设计更加复杂的应用,这样,我们可以把我们想要的rss feed轻易地加入到我们的podcast列表中.这个练习就交给我们的开发者吧.
整个项目的源码在:https://github.com/liu-xiao-guo/podcast
相关文章推荐
- ASP.NET动态网站制作(3)--css(2)
- LAMP基础上安装phpMyAdmin及WordPress
- C++中的单例模式
- 裴波那契数列 C++版
- java调用英飞拓摄像头监控接口打开摄像头,抓拍图片
- php性能优化
- 结对同学代码复审
- 已知两个链表 la和 lb,其元素值递增排序。编程将la和lb合并成一个递减有序(相同值元素只保留一个)的链表lc。(北方名校经典试题) 本题选做
- ruby 淘宝镜像
- JFinal 的源代码超具体的分析DB+ActiveRecord
- Item 38:通过组合表示"拥有"或"以...实现"的关系 Effective C++笔记
- C++_虚析构函数
- Python yield 使用浅析
- Java 进阶
- MyEclipse 添加MyBatis generator插件
- Unix高级环境编程
- duilib JS与C++的相互调用
- PHP数组问题
- java实现输入输出流代码分享
- java系统学习(十三) --------java中对数据的处理