首页 > 代码库 > Qt Quick应用开发介绍 10-12(动态界面, 实践学习, 总结和扩展)
Qt Quick应用开发介绍 10-12(动态界面, 实践学习, 总结和扩展)
Chapter10 UI Dynamics and Dynamic UI 动态界面
前面章节学习了在开发时添加item, 让它们invisible; 该怎么做可以让程序根据不同的数据和用户输入来有不同的显示? 这些变化可能比visibility复杂; 我们怎样才能做到让程序UI的动态变化更appealing吸引人, 甚至成为用户体验的一部分?
10.1 Using States 使用state
网络连接对于现在的版本中天气相关的部件是必须的; 它让网络数据可视化; 如果你的电脑不在线, 启动clock-n-weather应用的话, 只会看到时钟和空的内容;
这是因为WeatherModelItem没能拿到天气数据; 所以没有model项可以显示; 如果你在笔记本或移动设备上使用这个程序, 这种情况会经常发生; 你的程序需要处理没有网络的情况; 我们可以使用State来处理;
QtQuick里的每个item都有一个state属性, 保存了当前state的名字; 也有一个states属性, 是一个States的list; 这个属性包含了那个item所有已知的states; 列表中每个States有个string名字, 定义了一组property值; 需要的话, 它甚至可以包含script代码, 当那个state变化的时候被执行; item可以被设成一个state, 只要把选中的state的名称分配给state属性; refer to QML States
对于我们的程序, 会添加三种state:
- Offline, 开始阶段的初始化状态; 也应用到没有网络连接或程序处于离线状态;
- Loading, 网络连接可用, 但是WeatherModelItem还在加载天气数据; 这个state在网络连接很慢的时候有用; (e.g. 非宽带的移动设备)
- Live Weather, 更新的天气数据可用而且能够显示;
在Offline和Loading状态, 程序只会显示clock, 以一个较大的尺寸显示在屏幕中间; 当Live Weather状态激活, 程序就会显示天气数据;
由于我们的新states和WeatherModelItem的状态紧密相关, 必须把它们直接绑定; WeatherModelItem没有定义任何真实的state; 我们根据current或forecast模型的status, hijack抓取它的states属性来存储Offline, Loading, Live Weather值;
components/WeatherModelItem.qml
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 | XmlListModel { id: current source: root.source query: "/xml_api_reply/weather/current_conditions" XmlRole { name: "condition" ; query: "condition/@data/string()" } XmlRole { name: "temp_c" ; query: "temp_c/@data/string()" } onStatusChanged: { root.modelDataError = false if (status == XmlListModel.Error) { root.state = "Offline" root.statusMessage = "Error occurred: " + errorString() root.modelDataError = true //console.log("Weather Clock: Error reading current: " + root.statusMessage) } else if (status == XmlListModel.Ready) { // check if the loaded model is not empty, and post a message if (get(0) === undefined) { root.state = "Offline" root.statusMessage = "Invalid location \"" + root.location + "\"" root.modelDataError = true } else { root.state = "Live Weather" root.statusMessage = "Live current weather is available" } //console.log("Weather Clock: " + root.statusMessage) } else if (status == XmlListModel.Loading) { root.state = "Loading" root.statusMessage = "Current weather is loading..." //console.log("Weather Clock: " + root.statusMessage) } else if (status == XmlListModel.Null) { root.state = "Loading" root.statusMessage = "Current weather is empty..." //console.log("Weather Clock: " + root.statusMessage) } else { root.modelDataError = true console.log( "Weather Clock: unknown XmlListModel status:" + status) } } } |
确切的states是由主要item: WeatherClock.引入的; 这个item有两个新的子item, 持有所有要根据states以不同样子显示的元素;
-clockScreen, 显示一个较大的clock. 对应main item是Offline或Loading的state;
-weatherScreen, 显示clock和天气预报, 对应Live Weather的state, 基本上和clock-n-weather一样;
最后一步, 把WeatherClock的states和WeatherModelItem的state值绑定到一起:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 | states: [ State { name: "Offline" PropertyChanges {target: clockScreen; visible: true } PropertyChanges {target: weatherScreen; visible: false } }, State { name: "Live Weather" PropertyChanges {target: clockScreen; visible: false } PropertyChanges {target: weatherScreen; visible: true } }, State { name: "Loading" PropertyChanges {target: clockScreen; visible: true } PropertyChanges {target: weatherScreen; visible: false } PropertyChanges {target: busyIndicator; on: true } } ] |
我们的State定义包含了PropertyChanges, 能改变屏幕的visibility, 可以在Loading状态打开busyIndicator;
Loading的state可能会被多次激活; 如果clock没有显示秒, 整个程序可能表现的好像hang住了一样; 我们需要一个动画态的busy indicator(忙碌指示)来告诉用户程序还在运行; Qt example: RSS News Reader 提供了一个漂亮的例子; 我们可以用它来稍作改动; busyIndicator在Loading state变成可见的, 然后通知用户程序正在后台处理数据;
注意我们使用了新的forceOffline设置, 如果forceOffline设置为true, 程序停留在Offline状态, 不管weatherModelItem.有什么变化;
如果我们现在改变states, 改变立即生效; 在state改变时, 有了transitions过渡效果和animation effect动画效果, 程序会看起来更有吸引力;
10.2 Adding Animations 添加动画
动画不仅仅对可视化的效果有作用; 它们也可以充当一些基本特性, 而使用by other means其他方式可能会很难达到相同效果(e.g. busy indicator) QtQuick提供了丰富的动画框架, 易于使用; 更多的细节本章节可能无法覆盖, 但可以花些时间来理解怎样使用动画;
通常, 所有的动画会控制元素的一个或多个属性, 修改它的visual外观; 这个修改可以有多种动态效果而且在不同的时间跨度span产生; 可以有多个动画平行或依次地应用到相同或不同的元素上; 你可以根据属性的变化显式地或隐式地运行一个动画; 你也可以永久地分配一个给一个属性分配一个动画, 那样只要属性一变化, 动画就会开始; 虽然Qt有个普通的Animation元素, 但多数时间, 你可能会使用一个由QtQuick提供的预定义的动画元素: QML Animation and Transition Elements
添加animation到程序中很简单; 主要的挑战是去找出哪个animation适合使用, 怎么使用它们来组合出需要的视觉效果;
Animation和Transition密切相关, 其定义了一个元素怎样从一个State 变换到另一个; 大多数情况下, 一个transition包含一个animation;
Qt doc提供了animation和transition的总览, 以及使用细节--QML Animation and Transitions
下面的代码块展示了Offline和Live Weather状态间的两个transition--过渡变化;
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 | transitions: [ Transition { from: "Offline" to: "Live Weather" PropertyAnimation { target: weatherScreen property: "opacity" from: 0 to: 1 easing.type: Easing.Linear duration: 5000 } }, Transition { from: "Live Weather" to: "Offline" PropertyAnimation { target: clockScreen property: "opacity" from: 0 to: 1 easing.type: Easing.Linear duration: 5000 } } ] |
state改变会跟着weather数据而切换offline view和full view的visibility; 在上面, 增加了一个animation来改变opacity属性; 它会让screen淡出, 在5秒内消失;
Note 理论上, 一个微小的flickering(闪烁)会在transition开始的时候在屏幕上可见, 因为target元素会先变得全部可见, 就在它的opacity变成0之后, animation一开始的时候, ;
busy indicator的功能完全是基于animation的; 这里几乎没有其他代码的实现:
utils/BusyIndicator.qml
1 2 3 4 5 6 7 8 9 10 11 | Image { id: root property bool on: false source: "../content/resources/busy.png" visible: root.on NumberAnimation on rotation { running: root.on; from: 0; to: 360; loops: Animation.Infinite; duration: 1200 } } |
BusyIndicator可以这样加载:
1 2 3 4 5 6 | BusyIndicator { id: busyIndicator anchors.horizontalCenter: root.horizontalCenter anchors.bottom: statusText.top anchors.margins: Style.baseMargin } |
下一节将另一个动画, 在clock和weather上实现可视化效果;
10.3 Supporting the Landscape Mode 支持风景模式
如果程序是在移动设备上运行, 它应该有一个clockScreen层和weatherScreen层用来 tailored(适应)风景显示的方向; 因为clockScreen只包含一个item, 我们不需要对它做许多改动, weatherScreen的改动较大;
有一个有趣的方法可以简化实现, 使用Flow代替Colum; Flow将children动态地按照它自己的size来排列; 需要的话, 它会把children wrap到合适的行列中;
Flow有一个特性很好用; move属性上, 我们可以定义一个Transition, 在children在一个Flow中开始移动的时候应用; 我们使用一个NumberAnimation来应用到children的左边, 选择一个bounce(弹跳)效果, Easing.OutBounce给 easing.type:
1 2 3 4 5 6 7 | move: Transition { NumberAnimation { properties: "x,y" duration: 500 easing.type: Easing.OutBounce } } |
10.4 Finalizing the Main Item 完成Main项目
我们需要重做main item, 添加价格新特性; 首先我们从 clock-n-weather/ClockAndWeather.qml 中取出main item 然后添加animations和transitions;
另外, 重做的main item有三个button和一个status text在屏幕底下;
点击exitButton退出应用, 点击root item内部将不再起作用;
toggleStatesButton强制下线状态; 可以隐藏天气预报来使用屏幕空间放置一个更大的clock; 它也防止从Internet传递常规的数据;
configureButton显示configure元素, 它持有并且操作了configuration参数; main item将它们绑定到合适的属性上; 这实现了一种全局的程序状态;
status text根据states改变而更新;
完全代码: 略 WeatherClock/WeatherClock.qml
下一步
确实, 我们最后的程序还可以使用许多特性被增强和扩展; 我们选择了最小子集来覆盖教程里的范围, 下一节讨论一些挑选的增强效果;
---10---
Chapter11 Doing More, Learning More 实践能学到更多
11.1 Porting to Qt5 转到Qt5
Qt5包含新版本的QtQuick: 2.0; 另外, 由于模块化的关系, 有些预设的component中有些改动; 我们需要做两个基本改动:
1) 把import QtQuick 1.x改成import QtQuick 2.0;
2) 使用XmlListModel的时候我们需要添加 import QtQuick.XmlListModel 2.0
11.2 Porting to a mobile device 转到移动设备
把应用在Symbian Anna 或 Belle设备上运行很容易, 就像N9; 你可以使用template模板程序, 在QtCreator中创建一个新的项目; FIle->New File或Project, 在Application类目中选择Qt Quick Application(内建元素) 项目类型;
Note 这些步骤在QtCreator的project wizard中, 不同版本稍有不同;
wizard创建一个简单程序显示"Hello World", 包含C++代码和一些编译需要的文件;
可以用你的QML代码代替"Hello World"的QML:
- 把QML文件从WeatherClock文件夹包括Js, components, content. utils拷贝到项目文件夹下;
- 删除自动创建的main.qml, 把WeatherClock命名为main.qml;
- 把QML component和resources的相应目录设置好: 移除main.qml中的"../", 从./js/style.js中移除backgroundImage中的"../", 在Configure.qml中, 在source属性中的background值, 添加 "./ +"
-新的layout布局和尺寸要被裁剪为适合设备的350x640屏幕分辨率; e.g. NokiaN8; 如果设备有其他分辨率, 你需要调整尺寸相关的属性;
你可以在模拟器中编译运行, 观察portrait(肖像)和landscape(风景)模式;
11.3 Enhancements and New Features 功能增强以及新特性
-更好的configuration参数处理;
现在是把configuration参数保存在Configure组件中, 提供了一个UI; 所有的configuration改变在用户退出app时会丢失;
一个更好的实现应该是把Configure组件分成一个UI元素和一个configuration item; 后者的可以被其他需要使用configuration参数的item加载; 用户可以通过新的UI元素改变configuration参数;默认值加载以及在app退出时的保存, 可以由一个专用的item完成; 这个item使用了QtQuick的Offline Storage API/Qt Quick Local Storage QML Types; http://qt-project.org/doc/qt-4.8/qdeclarativeglobalobject.html
“Qt Quick Application Developer Guide for Desktop”在4.2 Store and Load Data from a Database Using Javascript 解释了细节; 程序首次运行的时候, 一组默认值存储在了database中; 下一次启动的时候, database中的值可以被读取, 分配给configuration item相应的属性; 所有这些可以在main.qml的 onCompleted handler中完成; 我们可以在点击exitButton调用Qt.quit()之前, 存储当前的configuration参数;
-Internationalization国际化
新版本的应用可以在多语言环境使用; 我们已经使用了qsTr()宏; Google weather data可以使用多语言请求, 这省下很多工作; 不幸的是,, 这个应用在这有个小问题; 天气图标是根据天气情况用英语命名的; 如果天气数据是其他语言, 图标会找不到. 因为文件名和天气名字不匹配; 一个方案是将文件名改成URLs, 使用根据天气数据而定的默认图标, 就像本地图标根据文件名来定一样;
-Using Mobility APIs to get the current location automatically 使用Mobility APIs来自动获取当前位置;
如果程序在移动设备上运行, 可以使用Mobility API http://doc.qt.digia.com/qtmobility/index.html 代替预定义的位置, 来自动获取当前位置;
-Using other weather feeds 使用其他的天气源
支持多一种天气源可能是个好主意; 多数源需要注册, 多数作为商业用途的是收费的; 你可以为你这版的应用考虑其他的feeds;
Weather Channel Weather APIs http://www.programmableweb.com/news/5-weather-apis-weatherbug-to-weather-channel/2009/04/15
Weather Underground Authbrand http://www.wunderground.com/autobrand/info.asp
Weather Underground Weather API http://www.wunderground.com/weather/api
下一步
下一章;, 总结
---11---
Chapter12 Lesson Learned and Further Reading 学习总结和扩展阅读
这个指南介绍了QtQuick的编程; 我们已经接触到了QtQuick的主要方面; 这些可以满足日常需求;
指南主要是帮助你开始了解, 指示哪里可以找到更多细节; 指南没有覆盖所有细节, 网络上可以看到Qt Document和其他资料;
有一个重点我们没有接触, 但是提到几次; 关于 C++扩展QtQuick, 给现有软件系统提供接口; 下面是相关文档:
QML for Qt Programmers http://qt-project.org/doc/qt-4.8/qtprogrammers.html
Qt Quick - Introduction to Qt Quick http://qt-project.org/doc/qt-4.8/qtbinding.html
Extending QML Functionalities using C++ http://qt-project.org/doc/qt-4.8/qml-extending.html
除了QtQuick example之外, 许多有趣的example在Qt培训材料中:
Qt Quick - Introduction to Qt Quick http://qt-project.org/videos/watch/qt_quick_introduction_to_qt_quick_part_1_4
Qt Quick - Rapid User Interface Prototyping http://qt-project.org/videos/watch/qt_quick_rapid_user_interface_prototyping
---12---
---YCR---