路徑視圖(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來控制可見的代理項。
PathView {
anchors.fill: parent
delegate: flipCardDelegate
model: 100
path: Path {
startX: root.width/2
startY: 0
PathAttribute { name: "itemZ"; value: 0 }
PathAttribute { name: "itemAngle"; value: -90.0; }
PathAttribute { name: "itemScale"; value: 0.5; }
PathLine { x: root.width/2; y: root.height*0.4; }
PathPercent { value: 0.48; }
PathLine { x: root.width/2; y: root.height*0.5; }
PathAttribute { name: "itemAngle"; value: 0.0; }
PathAttribute { name: "itemScale"; value: 1.0; }
PathAttribute { name: "itemZ"; value: 100 }
PathLine { x: root.width/2; y: root.height*0.6; }
PathPercent { value: 0.52; }
PathLine { x: root.width/2; y: root.height; }
PathAttribute { name: "itemAngle"; value: 90.0; }
PathAttribute { name: "itemScale"; value: 0.5; }
PathAttribute { name: "itemZ"; value: 0 }
}
pathItemCount: 16
preferredHighlightBegin: 0.5
preferredHighlightEnd: 0.5
}
代理如下面所示,使用了一些從PathAttribute中鏈接的屬性,itemZ,itemAngle和itemScale。需要注意代理鏈接的屬性只在wrapper中可用。因此,rotxs屬性在Rotation元素中定義為可訪問值。
另一個需要注意的是路徑視圖(PathView)鏈接的PathView.onPath屬性的用法。通常對于這個屬性都綁定為可見,這樣允許路徑視圖(PathView)緩衝不可見的元素。這不是通過剪裁處理來實現的,因為路徑視圖(PathView)的代理比其它的視圖,例如鏈表視圖(ListView)或者柵格視圖(GridView)放置更加隨意。
Component {
id: flipCardDelegate
Item {
id: wrapper
width: 64
height: 64
visible: PathView.onPath
scale: PathView.itemScale
z: PathView.itemZ
property variant rotX: PathView.itemAngle
transform: Rotation { axis { x: 1; y: 0; z: 0 } angle: wrapper.rotX; origin { x: 32; y: 32; } }
Rectangle {
anchors.fill: parent
color: "lightGray"
border.color: "black"
border.width: 3
}
Text {
anchors.centerIn: parent
text: index
font.pixelSize: 30
}
}
}
當在路徑視圖(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屬性使用。
import QtQuick 2.0
import QtQuick.XmlListModel 2.0
Item {
width: 300
height: 480
Component {
id: imageDelegate
Item {
width: listView.width
height: 400
Column {
Text {
text: title
}
Image {
source: imageSource
}
}
}
}
XmlListModel {
id: imageModel
source: "http://feeds.nationalgeographic.com/ng/photography/photo-of-the-day/"
query: "/rss/channel/item"
XmlRole { name: "title"; query: "title/string()" }
XmlRole { name: "imageSource"; query: "substring-before(substring-after(description/string(), 'img src=\"'), '\"')" }
}
ListView {
id: listView
anchors.fill: parent
model: imageModel
delegate: imageDelegate
}
}
有時,鏈表的數據需要劃分段。例如使用首字母來劃分聯系人,或者音樂。使用鏈表視圖可以把平面列表按類別劃分。
為了使用分段,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組件來代理顯示。
import QtQuick 2.0
Rectangle {
width: 300
height: 290
color: "white"
ListView {
anchors.fill: parent
anchors.margins: 20
clip: true
model: spaceMen
delegate: spaceManDelegate
section.property: "nation"
section.delegate: sectionDelegate
}
Component {
id: spaceManDelegate
Item {
width: 260
height: 20
Text {
anchors.left: parent.left
anchors.verticalCenter: parent.verticalCenter
anchors.leftMargin: 10
font.pixelSize: 12
text: name
}
}
}
Component {
id: sectionDelegate
Rectangle {
width: 260
height: 20
color: "lightGray"
Text {
anchors.left: parent.left
anchors.verticalCenter: parent.verticalCenter
anchors.leftMargin: 10
font.pixelSize: 12
font.bold: true
text: section
}
}
}
ListModel {
id: spaceMen
ListElement { name: "Abdul Ahad Mohmand"; nation: "Afganistan"; }
ListElement { name: "Marcos Pontes"; nation: "Brazil"; }
ListElement { name: "Alexandar Panayotov Alexandrov"; nation: "Bulgaria"; }
ListElement { name: "Georgi Ivanov"; nation: "Bulgaria"; }
ListElement { name: "Roberta Bondar"; nation: "Canada"; }
ListElement { name: "Marc Garneau"; nation: "Canada"; }
ListElement { name: "Chris Hadfield"; nation: "Canada"; }
ListElement { name: "Guy Laliberte"; nation: "Canada"; }
ListElement { name: "Steven MacLean"; nation: "Canada"; }
ListElement { name: "Julie Payette"; nation: "Canada"; }
ListElement { name: "Robert Thirsk"; nation: "Canada"; }
ListElement { name: "Bjarni Tryggvason"; nation: "Canada"; }
ListElement { name: "Dafydd Williams"; nation: "Canada"; }
}
}
一個模型視圖的性能很大程度上依賴于代理的創建。例如滾動下拉一個鏈表視圖時,代理從外部加入到視圖底部,並且從視圖頂部移出。如果設置剪裁(clip)屬性為false,並且代理項花了很多時間來初始化,用戶會感覺到視圖滾動體驗很差。
為了優化這個問題,你可以在滾動時使用像素來調整。使用cacheBuffer屬性,在上訴情況下的垂直滾動,它將會調整在鏈表視圖的上下需要預先準備好多少像素的代理項,結合異步加載圖像元素(Image),例如在它們進入視圖之前加載。
創建更多的代理項將會犧牲一些流暢的體驗,並且花更多的時間來初始化每個代理。這並不代表可以解決一些更加復雜的代理項的問題。在每次實例化代理時,它的內容都會被評估和編輯。這需要花費時間,如果它花費了太多的時間,它將會導致一個很差的滾動體驗。在一個代理中包含太多的元素也會降低滾動的性能。
為了補救這個問題,我們推薦使用動態加載元素。當它們需要時,可以初始化這些附加的元素。例如,一個展開代理可能推遲它的詳細內容的實例化,直到需要使用它時。每個代理中最好減少JavaScript的數量。將每個代理中復雜的JavaScript調用放在外面來實現。這將會減少每個代理在創建時編譯JavaScript。