注意
這章的源代碼可以在http://qmlbook.org/assets/中找到。
數組
在QtQuick中,數據經過model-view(模型-視圖)分離。對於每一個view(視圖),每一個數據元素的可視化都分給一個代理(delegate)。QtQuick附帶了一組預約義的模型與視圖。想要使用這個系統,必須理解這些類,而且知道如何建立合適的代理來得到正確的顯示和交互。性能優化
對於開發用戶界面,最重要的一方面是保持數據與可視化的分離。例如,一個電話薄可使用一個垂直文本鏈表排列或者使用一個網格聯繫人圖片排列。在這兩個案例中,數據都是相同的,可是可視化效果倒是不一樣的。這種方法一般被稱做model-view(模型-視圖)模式。在這種模式中,數據一般被稱做model(模型),可視化處理稱做view(視圖)。
在QML中,model(模型)與view(視圖)都經過delegate(代理)加入。功能劃分以下,model(模型)提供數據。對於每一個數據項,可能有多個值。在下面的例子中,每一個電話薄條目都有一個名字,一個圖片和一個號碼。數據在view(視圖)中被排列,每項使用一個delegate(代理)來實現可視化。view(視圖)的任務是排列這些delegate(代理),每一個delegate(代理)將model item(模型項)的值顯示給用戶。網絡
最基本的分離數據與顯示的方法是使用Repeater元素。它被用於實例化一組元素項,而且很容易與一個用於填充用戶界面的定位器相結合。
最基本的實現舉例,repeater元素用於實現子元素的標號。每一個子元素都擁有一個能夠訪問的屬性index,用於區分不一樣的子元素。在下面的例子中,一個repeater元素建立了10個子項,子項的數量由model屬性控制。對於每一個子項Rectangle包含了一個Text元素,你能夠將text屬性設置爲index的值,所以能夠看到子項的編號是0~9。併發
這是一個不錯的編號列表,有時咱們想顯示一些更復雜的數據。使用一個JavaScript序列來替換整形變量model的值能夠達到咱們的目的。序列可使用任何類型的內容,能夠是字符串,整數,或者對象。在下面的例子中,使用了一個字符串鏈表。咱們仍然使用index的值做爲變量,而且咱們也訪問modelData中包含的每一個元素的數據。app
將數據暴露成一組序列,你能夠經過標號迅速的找到你須要的信息。想象一下這個模型的草圖,這是一個最簡單的模型,也是一般都會使用的模型,ListModel(鏈表模型)。一個鏈表模型由許多ListElement(鏈表元素)組成。在每一個鏈表元素中,能夠綁定值到屬性上。例如在下面這個例子中,每一個元素都提供了一個名字和一個顏色。
每一個元素中的屬性綁定鏈接到repeater實例化的子項上。這意味着變量name和surfaceColor能夠被repeater建立的每一個Rectangle和Text項引用。這不只能夠方便的訪問數據,也可使源代碼更加容易閱讀。surfaceColor是名字左邊園的顏色,而不是模糊的數據序列列i或者行j。異步
repeater的內容的每一個子項實例化時綁定了默認的屬性delegate(代理)。這意味着例1(第一個代碼段)的代碼與下面顯示的代碼是相同的。注意,惟一的不一樣是delegate屬性名,將會在後面詳細講解。函數
Repeater元素適合有限的靜態數據,可是在真正使用時,模型一般更加複雜和龐大,咱們須要一個更加智能的解決方案。QtQuick提供了ListView和GridView元素,這兩個都是基於Flickable(可滑動)區域的元素,所以用戶能夠放入更大的數據。同時,它們限制了同時實例化的代理數量。對於一個大型的模型,這意味着在同一個場景下只會加載有限的元素。性能
這兩個元素的用法很是相似,咱們由ListView開始,而後會描述GridView的模型起點來進行比較。
ListView與Repeater元素像素,它使用了一個model,使用delegate來實例化,而且在兩個delegate之間可以設置間隔sapcing。下面的列表顯示了怎樣設置一個簡單的鏈表。學習
若是模型包含的數據比屏幕上顯示的更多,ListView元素只會顯示部分的鏈表內容。而後因爲QtQuick的默認行爲致使的問題,列表視圖不會限制被顯示的代理項(delegates)只在限制區域內顯示。這意味着代理項能夠在列表視圖外顯示,用戶能夠看見在列表視圖外動態的建立和銷燬這些代理項(delegates)。爲了防止這個問題,ListView經過設置clip屬性爲true,來激活裁剪功能。下面的圖片展現了這個結果,左邊是clip屬性設置爲false的對比。優化
對於用戶,ListView(列表視圖)是一個滾動區域。它支持慣性滾動,這意味着它能夠快速的翻閱內容。默認模式下,它能夠在內容最後繼續伸展,而後反彈回去,這個信號告訴用戶已經到達內容的末尾。
視圖末尾的行爲是由到boundsBehavior屬性的控制的。這是一個枚舉值,而且能夠配置爲默認的Flickable.DragAndOvershootBounds,視圖能夠經過它的邊界線來拖拽和翻閱,配置爲Flickable.StopAtBounds,視圖將再也不能夠移動到它的邊界線以外。配置爲Flickable.DragOverBounds,用戶能夠將視圖拖拽到它的邊界線外,可是在邊界線上翻閱將無效。
使用snapMode屬性能夠限制一個視圖內元素的中止位置。默認行爲下是ListView.NoSnap,容許視圖內元素在任何位置中止。將snapMode屬性設置爲ListView.SnapToItem,視圖頂部將會與元素對象的頂部對齊排列。使用ListView.SnapOneItem,當鼠標或者觸摸釋放時,視圖將會中止在第一個可見的元素,這種模式對於瀏覽頁面很是便利。
默認的鏈表視圖只提供了一個垂直方向的滾動條,可是水平滾動條也是須要的。鏈表視圖的方向由屬性orientation控制。它可以被設置爲默認值ListView.Vertical或者ListView.Horizontal。下面是一個水平鏈表視圖。
按照上面的設置,水平鏈表視圖默認的元素順序方向是由左到右。能夠經過設置layoutDirection屬性來控制元素順序方向,它能夠設置爲Qt.LeftToRight或者Qt.RightToLeft。
當使用基於觸摸方式的鏈表視圖時,默認提供的視圖已經足夠使用。在使用鍵盤甚至僅僅經過方向鍵選擇一個元素的場景下,須要有標識當前選中元素的機制。在QML中,這被叫作高亮。
視圖支持設置一個當前視圖中顯示代理元素中的高亮代理。它是一個附加的代理元素,這個元素僅僅只實例化一次,並移動到與當前元素相同的位置。
在下面例子的演示中,有兩個屬性來完成這個工做。首先是focus屬性設置爲true,它設置鏈表視圖可以得到鍵盤焦點。而後是highlight屬性,指出使用的高亮代理元素。高亮代理元素的x,y與height屬性由當前元素指定。若是寬度沒有特別指定,當前元素的寬度也能夠用於高亮代理元素。
在例子中,ListView.view.width屬性被綁定用於高亮元素的寬度。關於代理元素的使綁定屬性將在後面的章節討論,可是最好知道相同的綁定屬性也能夠用於高亮代理元素。
當使用高亮與鏈表視圖(ListView)結合時,一些屬性能夠用來控制它的行爲。highlightRangeMode控制了高亮如何影響視圖中當前的顯示。默認設置ListView.NoHighLighRange意味着高亮與視圖中的元素距離不相關。
ListView.StrictlyEnforceRnage確保了高亮始終可見,若是某個動做嘗試將高亮移出當前視圖可見範圍,當前元素將會自動切換,確保了高亮始終可見。
ListView.ApplyRange,它嘗試保持高亮代理始終可見,可是不會強制切換當前元素始終可見。若是在須要的狀況下高亮代理容許被移出當前視圖。
在默認配置下,視圖負責高亮移動到指定位置,移動的速度與大小的改變可以被控制,使用一個速度值或者一個動做持續時間來完成它。這些屬性包括highlightMoveSpeed,highlightMoveDuration,highlightResizeSpeed和highlightResizeDuration。默認下速度被設置爲每秒400像素,動做持續時間爲-1,代表速度和距離控制了動做的持續時間。若是速度與動做持續時間都被設置,動畫將會採用速度較快的結果來完成。
爲了更加詳細的控制高亮的移動,highlightFollowCurrentItem屬性設置爲false。這意味着視圖將再也不負責高亮代理的移動。取而代之能夠經過一個行爲(Bahavior)或者一個動畫來控制它。
在下面的例子中,高亮道理的y座標屬性與ListView.view.currentItem.y屬性綁定。這確保了高亮始終跟隨當前元素。然而,因爲咱們沒有讓視圖來移動這個高亮代理,咱們須要控制這個元素如何移動,經過Behavior on y來完成這個操做,在下面的例子中,移動分爲三步完成:淡出,移動,淡入。注意怎樣使用SequentialAnimation和PropertyAnimation元素與NumberAnimation結合建立更加複雜的移動效果。
Component { id: highlightComponent Item { width: ListView.view.width height: ListView.view.currentItem.height y: ListView.view.currentItem.y Behavior on y { SequentialAnimation { PropertyAnimation { target: highlightRectangle; property: "opacity"; to: 0; duration: 200 } NumberAnimation { duration: 100 } PropertyAnimation { target: highlightRectangle; property: "opacity"; to: 1; duration: 200 } } } Rectangle { id: highlightRectangle anchors.fill: parent color: "lightGreen" } } }
這一節是鏈表視圖最後的內容,咱們可以向鏈表視圖中插入一個頁眉(header)元素和一個頁腳(footer)元素。這部分是鏈表的開始或者結尾處被做爲代理元素特殊的區域。對於一個水平鏈表視圖,不會存在頁眉或者頁腳,可是也有開始和結尾處,這取決於layoutDirection的設置。
下面這個例子展現瞭如何使用一個頁眉和頁腳來突出鏈表的開始與結尾。這些特殊的鏈表元素也有其它的做用,例如,它們可以保持鏈表中的按鍵加載更多的內容。
import QtQuick 2.0 Rectangle { width: 80 height: 300 color: "white" ListView { anchors.fill: parent anchors.margins: 20 clip: true model: 4 delegate: numberDelegate spacing: 5 header: headerComponent footer: footerComponent } Component { id: headerComponent Rectangle { width: 40 height: 20 color: "yellow" } } Component { id: footerComponent Rectangle { width: 40 height: 20 color: "red" } } Component { id: numberDelegate Rectangle { width: 40 height: 40 color: "lightGreen" Text { anchors.centerIn: parent font.pixelSize: 10 text: index } } } }
注意
頁眉與頁腳代理元素不遵循鏈表視圖(ListView)的間隔(spacing)屬性,它們被直接放在相鄰的鏈表元素之上或之下。這意味着頁眉與頁腳的間隔必須經過頁眉與頁腳元素本身設置。
使用網格視圖(GridView)與使用鏈表視圖(ListView)的方式很是相似。真正不一樣的地方是網格視圖(GridView)使用了一個二維數組來存放元素,而鏈表視圖(ListView)是使用的線性鏈表來存放元素。
與鏈表視圖(ListView)比較,網格視圖(GridView)不依賴於元素間隔和大小來配置元素。它使用單元寬度(cellWidth)與單元高度(cellHeight)屬性來控制數組內的二維元素的內容。每一個元素從左上角開始依次放入單元格。
import QtQuick 2.0 Rectangle { width: 240 height: 300 color: "white" GridView { anchors.fill: parent anchors.margins: 20 clip: true model: 100 cellWidth: 45 cellHeight: 45 delegate: numberDelegate } Component { id: numberDelegate Rectangle { width: 40 height: 40 color: "lightGreen" Text { anchors.centerIn: parent font.pixelSize: 10 text: index } } } }
一個網格視圖(GridView)也包含了頁腳與頁眉,也可使用高亮代理而且支持捕捉模式(snap mode)的多種反彈行爲。它也可使用不一樣的方向(orientations)與定向(directions)來定位。
定向使用flow屬性來控制。它能夠被設置爲GridView.LeftToRight或者GridView.TopToBottom。模型的值從左往右向網格中填充,行添加是從上往下。視圖使用一個垂直方向的滾動條。後面添加的元素也是由上到下,由左到右。
附加還有flow屬性和layoutDirection屬性,可以適配網格從左到右或者從右到左,這依賴於你使用的設置值。
當使用模型與視圖來自定義用戶界面時,代理在建立顯示時扮演了大量的角色。在模型中的每一個元素經過代理來實現可視化,用戶真實可見的是這些代理元素。
每一個代理訪問到索引號或者綁定的屬性,一些是來自數據模型,一些來自視圖。來自模型的數據將會經過屬性傳遞到代理。來自視圖的數據將會經過屬性傳遞視圖中與代理相關的狀態信息。
一般使用的視圖綁定屬性是ListView.isCurrentItem和ListView.view。第一個是一個布爾值,標識這個元素是不是視圖當前元素,這個值是隻讀的,引用自當前視圖。經過訪問視圖,能夠建立可複用的代理,這些代理在被包含時會自動匹配視圖的大小。在下面這個例子中,每一個代理的width(寬度)屬性與視圖的width(寬度)屬性綁定,每一個代理的背景顏色color依賴於綁定的屬性ListView.isCurrentItem屬性。
import QtQuick 2.0 Rectangle { width: 120 height: 300 color: "white" ListView { anchors.fill: parent anchors.margins: 20 clip: true model: 100 delegate: numberDelegate spacing: 5 focus: true } Component { id: numberDelegate Rectangle { width: ListView.view.width height: 40 color: ListView.isCurrentItem?"gray":"lightGray" Text { anchors.centerIn: parent font.pixelSize: 10 text: index } } } }
若是在模型中的每一個元素與一個動做相關,例如點擊做用於一個元素時,這個功能是代理完成的。這是由事件管理分配給視圖的,這個操做控制了視圖中元素的導航,代理控制了特定元素上的動做。
最基礎的方法是在每一個代理中建立一個MouseArea(鼠標區域)而且響應onClicked信號。在後面章節中將會演示這個例子。
在某些狀況下,視圖中的顯示內容會隨着時間而改變。因爲模型數據的改變,元素會添加或者移除。在這些狀況下,一個比較好的作法是使用可視化隊列給用戶一個方向的感受來幫助用戶知道哪些數據被加入或者移除。
爲了方便使用,QML視圖爲每一個代理綁定了兩個信號,onAdd和onRemove。使用動畫鏈接它們,能夠方便建立識別哪些內容被添加或刪除的動畫。
下面這個例子演示瞭如何動態填充一個鏈表模型(ListModel)。在屏幕下方,有一個添加新元素的按鈕。當點擊它時,會調用模型的append方法來添加一個新的元素。這個操做會觸發視圖建立一個新的代理,併發送GridView.onAdd信號。SequentialAnimation隊列動畫與這個信號鏈接綁定,使用代理的scale屬性來放大視圖元素。
當視圖中的一個代理點擊時,將會調用模型的remove方法將一個元素從模型中移除。這個操做將會致使GridView.onRemove信號的發送,觸發另外一個SequentialAnimation。這時,代理的銷燬將會延遲直到動畫完成。爲了完成這個操做,PropertyAction元素須要在動畫前設置GridView.delayRemove屬性爲true,並在動畫後設置爲false。這樣確保了動畫在代理項移除前完成。
import QtQuick 2.0 Rectangle { width: 480 height: 300 color: "white" ListModel { id: theModel ListElement { number: 0 } ListElement { number: 1 } ListElement { number: 2 } ListElement { number: 3 } ListElement { number: 4 } ListElement { number: 5 } ListElement { number: 6 } ListElement { number: 7 } ListElement { number: 8 } ListElement { number: 9 } } Rectangle { anchors.left: parent.left anchors.right: parent.right anchors.bottom: parent.bottom anchors.margins: 20 height: 40 color: "darkGreen" Text { anchors.centerIn: parent text: "Add item!" } MouseArea { anchors.fill: parent onClicked: { theModel.append({"number": ++parent.count}); } } property int count: 9 } GridView { anchors.fill: parent anchors.margins: 20 anchors.bottomMargin: 80 clip: true model: theModel cellWidth: 45 cellHeight: 45 delegate: numberDelegate } Component { id: numberDelegate Rectangle { id: wrapper width: 40 height: 40 color: "lightGreen" Text { anchors.centerIn: parent font.pixelSize: 10 text: number } MouseArea { anchors.fill: parent onClicked: { if (!wrapper.GridView.delayRemove) theModel.remove(index); } } GridView.onRemove: SequentialAnimation { PropertyAction { target: wrapper; property: "GridView.delayRemove"; value: true } NumberAnimation { target: wrapper; property: "scale"; to: 0; duration: 250; easing.type: Easing.InOutQuad } PropertyAction { target: wrapper; property: "GridView.delayRemove"; value: false } } GridView.onAdd: SequentialAnimation { NumberAnimation { target: wrapper; property: "scale"; from: 0; to: 1; duration: 250; easing.type: Easing.InOutQuad } } } } }
在使用鏈表時一般會使用當前項激活時展開的機制。這個操做能夠被用於動態的將當前項目填充到整個屏幕來添加一個新的用戶界面,或者爲鏈表中的當前項提供更多的信息。
在下面的例子中,當點擊鏈表項時,鏈表項都會展開填充整個鏈表視圖(ListView)。額外的間隔區域被用於添加更多的信息,這種機制使用一個狀態來控制,當一個鏈表項展開時,代理項都能輸入expanded(展開)狀態,在這種狀態下一些屬性被改變。
首先,包裝器(wrapper)的高度(height)被設置爲鏈表視圖(ListView)的高度。標籤圖片被放大而且下移,使圖片從小圖片的位置移向大圖片的位置。除了這些以外,兩個隱藏項,實際視圖(factsView)與關閉按鍵(closeButton)切換它的opactiy(透明度)顯示出來。最後設置鏈表視圖(ListView)。
設置鏈表視圖(ListView)包含了設置內容Y座標(contentsY),這是視圖頂部可見的部分代理的Y軸座標。另外一個變化是設置視圖的交互(interactive)爲false。這個操做阻止了視圖的移動,用戶再也不可以經過滾動條切換當前項。
因爲設置第一個鏈表項爲可點擊,向它輸入一個expanded(展開)狀態,致使了它的代理項被填充到整個鏈表而且內容重置。當點擊關閉按鈕時,清空狀態,致使它的代理項返回上一個狀態,而且從新設置鏈表視圖(ListView)有效。
import QtQuick 2.0 Item { width: 300 height: 480 ListView { id: listView anchors.fill: parent delegate: detailsDelegate model: planets } ListModel { id: planets ListElement { name: "Mercury"; imageSource: "images/mercury.jpeg"; facts: "Mercury is the smallest planet in the Solar System. It is the closest planet to the sun. It makes one trip around the Sun once every 87.969 days." } ListElement { name: "Venus"; imageSource: "images/venus.jpeg"; facts: "Venus is the second planet from the Sun. It is a terrestrial planet because it has a solid, rocky surface. The other terrestrial planets are Mercury, Earth and Mars. Astronomers have known Venus for thousands of years." } ListElement { name: "Earth"; imageSource: "images/earth.jpeg"; facts: "The Earth is the third planet from the Sun. It is one of the four terrestrial planets in our Solar System. This means most of its mass is solid. The other three are Mercury, Venus and Mars. The Earth is also called the Blue Planet, 'Planet Earth', and 'Terra'." } ListElement { name: "Mars"; imageSource: "images/mars.jpeg"; facts: "Mars is the fourth planet from the Sun in the Solar System. Mars is dry, rocky and cold. It is home to the largest volcano in the Solar System. Mars is named after the mythological Roman god of war because it is a red planet, which signifies the colour of blood." } } Component { id: detailsDelegate Item { id: wrapper width: listView.width height: 30 Rectangle { anchors.left: parent.left anchors.right: parent.right anchors.top: parent.top height: 30 color: "#ffaa00" Text { anchors.left: parent.left anchors.verticalCenter: parent.verticalCenter font.pixelSize: parent.height-4 text: name } } Rectangle { id: image color: "black" anchors.right: parent.right anchors.top: parent.top anchors.rightMargin: 2 anchors.topMargin: 2 width: 26 height: 26 Image { anchors.fill: parent fillMode: Image.PreserveAspectFit source: imageSource } } MouseArea { anchors.fill: parent onClicked: parent.state = "expanded" } Item { id: factsView anchors.top: image.bottom anchors.left: parent.left anchors.right: parent.right anchors.bottom: parent.bottom opacity: 0 Rectangle { anchors.fill: parent color: "#cccccc" Text { anchors.fill: parent anchors.margins: 5 clip: true wrapMode: Text.WordWrap font.pixelSize: 12 text: facts } } } Rectangle { id: closeButton anchors.right: parent.right anchors.top: parent.top anchors.rightMargin: 2 anchors.topMargin: 2 width: 26 height: 26 color: "red" opacity: 0 MouseArea { anchors.fill: parent onClicked: wrapper.state = "" } } states: [ State { name: "expanded" PropertyChanges { target: wrapper; height: listView.height } PropertyChanges { target: image; width: listView.width; height: listView.width; anchors.rightMargin: 0; anchors.topMargin: 30 } PropertyChanges { target: factsView; opacity: 1 } PropertyChanges { target: closeButton; opacity: 1 } PropertyChanges { target: wrapper.ListView.view; contentY: wrapper.y; interactive: false } } ] transitions: [ Transition { NumberAnimation { duration: 200; properties: "height,width,anchors.rightMargin,anchors.topMargin,opacity,contentY" } } ] } } }
這個技術展現了展開代理來填充視圖可以簡單的經過代理的形變來完成。例如當瀏覽一個歌曲的鏈表時,能夠經過放大當前項來對該項添加更多的說明。
路徑視圖(PathView)很是強大,但也很是複雜,這個視圖由QtQuick提供。它建立了一個可讓子項沿着任意路徑移動的視圖。沿着相同的路徑,使用縮放(scale),透明(opacity)等元素能夠更加詳細的控制過程。
當使用路徑視圖(PathView)時,你必須定義一個代理和一個路徑。在這些之上,路徑視圖(PathView)自己也能夠自定義一些屬性的區間。一般會使用pathItemCount屬性,它控制了一次可見的子項總數。preferredHighLightBegin屬性控制了高亮區間,preferredHighlightEnd與highlightRangeMode,控制了當前項怎樣沿着路徑顯示。
在關注高亮區間以前,咱們必須先看看路徑(path)這個屬性。路徑(path)屬性使用一個路徑(path)元素來定義路徑視圖(PathView)內代理的滾動路徑。路徑使用startx與starty屬性來連接路徑(path)元素,例如PathLine,PathQuad和PathCubic。這些元素都使用二維數組來構造路徑。
當路徑定義好以後,可使用PathPercent和PathAttribute元素來進一步設置。它們被放置在路徑元素之間,而且爲通過它們的路徑和代理提供更加細緻的控制。PathPercent提供瞭如何控制每一個元素之間覆蓋區域部分的路徑,而後反過來控制分佈在這條路徑上的代理元素,它們被按比例的分佈播放。
preferredHightlightBegin與preferredHighlightEnd屬性由PathView(路徑視圖)輸入到圖片元素中。它們的值在0~1之間。結束值大於等於開始值。例如設置這些屬性值爲0.5,當前項只會顯示當前百分之50的圖像在這個路徑上。
在Path中,PathAttribute元素也是被放置在元素之間的,就像PathPercent元素。它們可讓你指定屬性的值而後插入的路徑中去。這些屬性與代理綁定能夠用來控制任意的屬性。
下面這個例子展現了路徑視圖(PathView)如何建立一個卡片視圖,而且用戶能夠滑動它。咱們使用了一些技巧來完成這個例子。路徑由PathLine元素組成。使用PathPercent元素,它確保了中間的元素居中,而且給其它的元素提供了足夠的空間。使用PathAttribute元素來控制旋轉,大小和深度值(z-value)。
在這個路徑之上(path),須要設置路徑視圖(PathView)的pathItemCount屬性。它控制了路徑的濃密度。路徑視圖的路徑(PathView.onPath)使用preferredHighlightBegin與preferredHighlightEnd來控制可見的代理項。
代理以下面所示,使用了一些從PathAttribute中連接的屬性,itemZ,itemAngle和itemScale。須要注意代理連接的屬性只在wrapper中可用。所以,rotxs屬性在Rotation元素中定義爲可訪問值。
另外一個須要注意的是路徑視圖(PathView)連接的PathView.onPath屬性的用法。一般對於這個屬性都綁定爲可見,這樣容許路徑視圖(PathView)緩衝不可見的元素。這不是經過剪裁處理來實現的,由於路徑視圖(PathView)的代理比其它的視圖,例如鏈表視圖(ListView)或者柵格視圖(GridView)放置更加隨意。
當在路徑視圖(PathView)上使用圖像轉換或者其它更加複雜的元素時,有一個性能優化的技巧是綁定圖像元素(Image)的smooth屬性與PathView.view.moving屬性。這意味着圖像在移動時可能不夠完美,可是可以比較平滑的轉換。當視圖在移動時,對於平滑縮放的處理是沒有意義的,由於用戶根本看不見這個過程。
因爲XML是一種常見的數據格式,QML提供了XmlListModel元素來包裝XML數據。這個元素可以獲取本地或者網絡上的XML數據,而後經過XPath解析這些數據。
下面這個例子展現了從RSS流中獲取圖片,源屬性(source)引用了一個網絡地址,這個數據會自動下載。
當數據下載完成後,它會被加工做爲模型的子項。查詢屬性(query)是一個XPath代理的基礎查詢,用來建立模型項。在這個例子中,這個路徑是/rss/channel/item,因此,在一個模型子項建立後,每個子項的標籤,都包含了一個頻道標籤,包含一個RSS標籤。
每個模型項,一些規則須要被提取,由XmlRole元素來代理。每個規則都須要一個名稱,這樣代理纔可以經過屬性綁定來訪問。每一個這樣的屬性的值都經過XPath查詢來肯定。例如標題屬性(title)符合title/string()查詢,返回內容中在之間的值。
圖像源屬性(imageSource)更加有趣,由於它不只僅是從XML中提取字符串,也須要加載它。在流數據的支持下,每一個子項包含了一個圖片。使用XPath的函數substring-after與substring-before,能夠提取本地的圖片資源。這樣imageSource屬性就能夠直接被做爲一個Image元素的source屬性使用。
有時,鏈表的數據須要劃分段。例如使用首字母來劃分聯繫人,或者音樂。使用鏈表視圖能夠把平面列表按類別劃分。
爲了使用分段,section.property與section.criteria必須安裝。section.property定義了哪些屬性用於內容的劃分。在這裏,最重要的是知道每一段由哪些連續的元素構成,不然相同的屬性名可能出如今幾個不一樣的地方。
section.criteria可以被設置爲ViewSection.FullString或者ViewSection.FirstCharacter。默認下使用第一個值,可以被用於模型中有清晰的分段,例如音樂專輯。第二個是使用一個屬性的首字母來分段,這說明任何屬性均可以被使用。一般的例子是用於聯繫人名單的姓。
當段被定義好後,每一個子項可以使用綁定屬性ListView.section,ListView.previousSection與ListView.nextSection來訪問。使用這些屬性,能夠檢測段的第一個與最後一個子項。
使用鏈表視圖(ListView)的section.delegate屬性能夠給段指定代理組件。它可以建立段標題,而且能夠在任意子項以前插入這個段代理。使用綁定屬性section能夠訪問當前段的名稱。
下面這個例子使用國際分類展現了分段的一些概念。國籍(nation)做爲section.property,段代理組件(section.delegate)使用每一個國家做爲標題。在每一個段中,spacemen模型中的名字使用spaceManDelegate組件來代理顯示。
一個模型視圖的性能很大程度上依賴於代理的建立。例如滾動下拉一個鏈表視圖時,代理從外部加入到視圖底部,而且從視圖頂部移出。若是設置剪裁(clip)屬性爲false,而且代理項花了不少時間來初始化,用戶會感受到視圖滾動體驗不好。
爲了優化這個問題,你能夠在滾動時使用像素來調整。使用cacheBuffer屬性,在上訴狀況下的垂直滾動,它將會調整在鏈表視圖的上下須要預先準備好多少像素的代理項,結合異步加載圖像元素(Image),例如在它們進入視圖以前加載。
建立更多的代理項將會犧牲一些流暢的體驗,而且花更多的時間來初始化每一個代理。這並不表明能夠解決一些更加複雜的代理項的問題。在每次實例化代理時,它的內容都會被評估和編輯。這須要花費時間,若是它花費了太多的時間,它將會致使一個不好的滾動體驗。在一個代理中包含太多的元素也會下降滾動的性能。
爲了補救這個問題,咱們推薦使用動態加載元素。當它們須要時,能夠初始化這些附加的元素。例如,一個展開代理可能推遲它的詳細內容的實例化,直到須要使用它時。每一個代理中最好減小JavaScript的數量。將每一個代理中複雜的JavaScript調用放在外面來實現。這將會減小每一個代理在建立時編譯JavaScript。
在這個章節中,咱們學習了模型,視圖與代理。每一個數據的入口是模型,視圖經過可視化代理來實現數據的可視化。將數據從顯示中分離出來。一個模型能夠是一個整數,提供給代理使用的索引值(index )。若是JavaScript數組被做爲一個模型,模型數據變量(modelData)表明了數組的數據的當前索引。對於更加複雜的狀況,每一個數據項須要提供多個值,使用鏈表模型(ListModel)與鏈表元素(ListElement)是一個更好的解決辦法。對於靜態模型,一個Repeater能夠被用做視圖。它能夠很是方便的使用行(Row),列(Column),柵格(Grid),或者流(Flow)來建立用戶界面。對於動態或者大的數據模型,使用ListView或者GridView更加適合。它們會在須要時動態的建立代理,減小在場景下一次顯示的元素的數量。在視圖中的代理能夠與數據模型中的屬性靜態綁定,或者動態綁定。使用視圖的onAdd與onRemove信號,能夠動態播放的它們的顯示與消失。