您的位置:首页 > 编程语言

创建可以重复利用的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.其设计如下:

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
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: