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 Typeshttp://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--- 


發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章