QML中動態與靜態模型應用詳解
前言
對於開發用戶界面,最重要的就是保持數據與UI分離。數據通常被稱為為model,可視化處理稱作view。在QML中,model與view都通過delegate連接起來。功能劃分如下,model提供數據,對於每個數據項,可能有很多個值。顯示在view(視圖)中的每項數據,都是通過delegate(代理)來實現可視化的。view(視圖)的任務是排列這些delegate(代理),每個delegate(代理)將model item(模型項)的值呈現給用戶。
一個模型可以是一個整數,提供給代理使用的索引值(index).如果JavaScript數組被作為一個模型,模型數據變量(modelData)代表瞭數組的數據的當前索引。對於更加復雜的情況,每個數據項需要提供多個值,可以使用ListModel與ListElement。
對於靜態模型,一個Repeater可以被用作視圖。它可以非常方便的使用行(Row),列(Column),柵格(Grid),或者流(Flow)來創建用戶界面。對於動態或者更大的數據模型,使用ListView或者GridView更加合適。它們會在需要時動態的創建代理,減少在場景下一次顯示的元素的數量。
在視圖中的代理可以與數據模型中的屬性靜態綁定或者動態綁定。使用onAdd與onRemove信號,可以動態的播放他們的添加和移除的特效。
靜態模型
通過Repeater來作視圖,用來創建一些靜態的顯示界面。
import QtQuick 2.12 import QtQuick.Window 2.12 Window { visible: true width: 640 height: 480 title: qsTr("靜態模型") //靜態顯示單一的數據模型 Column{ id: column1 spacing: 10 Repeater{ model: 4 Rectangle{ width:300 height: 40 radius: 3 color: "lightBlue" Text { anchors.centerIn: parent text: index } } } } //靜態顯示列表數據模型 Column{ id: column2 anchors.top: column1.bottom anchors.topMargin: 10 spacing: 10 Repeater{ model: ["Enterpris","Colombia","Challenger","Discover"] Rectangle{ width:300 height: 40 radius: 3 color: "lightBlue" Text { anchors.centerIn: parent text: index + ":" + modelData } } } } //使用多元素的ListModel Row{ id: listModelItem anchors.top: column2.bottom anchors.topMargin: 10 spacing: 10 Repeater{ model: ListModel{ ListElement{name : "項目1";surfaceColor: "gray";} ListElement{name : "項目2";surfaceColor: "orange";} ListElement{name : "項目3";surfaceColor: "red";} } Rectangle{ width: 150 height: 40 radius: 3 color: "lightBlue" Text { anchors.left: circleItem.right anchors.leftMargin: 10 anchors.centerIn: parent text: name } Rectangle{ id: circleItem anchors.left: parent.left anchors.verticalCenter: parent.verticalCenter anchors.leftMargin: 4 width: 32 height: 32 radius: 16 border.color: "black" border.width: 2 color: surfaceColor } } } } Row{ spacing: 5 anchors.top: listModelItem.bottom anchors.topMargin: 10 Repeater{ model:4 delegate: Rectangle{ width: 150 height: 40 radius: 3 color: "lightBlue" Text { anchors.centerIn: parent text: index } } } } }
顯示效果如下圖所示:
動態模型
Repeator元素適合有限的靜態元素,但是真正使用的時候,模型通常更加復雜和龐大。QtQuick提供瞭ListView和GridView元素,這兩個都是基於Flickable區域的元素,因此用戶可以放入更大的數據。
import QtQuick 2.12 import QtQuick.Window 2.12 Window { visible: true width: 640 height: 480 title: qsTr("動態模型") Rectangle{ id: rowView width: 80 height: 300 color: "white" ListView{ anchors.fill: parent anchors.margins: 20 //是否對邊界進行裁剪 clip: true model: 5 delegate: numberDelegate //列表顯示是水平還是垂直 orientation: ListView.Vertical //focus: true spacing: 10 //頁眉和頁腳 header: headerComponent footer: footerComponent } Component{ id: numberDelegate //必須使用Item做為基本元素 Rectangle{ width: 40 height: 40 color:"lightGreen" Text { anchors.centerIn: parent font.pixelSize: 15 text: index } } } Component{ id: headerComponent Rectangle{ width: 40 height: 20 color: "yellow" } } Component{ id: footerComponent Rectangle{ width: 40 height: 20 color: "yellow" } } } Rectangle{ id: gridView width: 240 height: 300 color: "white" anchors.left: rowView.right GridView{ anchors.fill: parent anchors.margins: 20 //是否對邊界進行裁剪 clip: true model: 100 delegate: gridDelegate cellHeight: 45 cellWidth: 45 focus: true } Component{ id: gridDelegate //必須使用Item做為基本元素 Rectangle{ width: 40 height: 40 color: GridView.isCurrentItem? "Green":"lightGreen" Text { anchors.centerIn: parent font.pixelSize: 10 text: index } } } } }
顯示效果如下圖所示:
有時候我們需要根據需要動態的向數據模型中添加或者刪除元素,這時候我們需要瞭解元素添加和移除的接口。為瞭方便使用,QML為每個視圖綁定瞭兩個信號,onAdd和onRemove.使用動畫連接它們,可以方便的創建識別哪些內容被添加或刪除的動畫。
import QtQuick 2.12 import QtQuick.Window 2.12 Window { visible: true width: 640 height: 480 title: qsTr("動態添加和刪除元素") 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} } 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 } } } } } }
顯示效果如下圖所示:
在使用鏈表時通常會使用當前項激活時展開的機制。這個操作可以被用於動態的將當前項目填充到整個屏幕來添加一個新的用戶界面,或者為鏈表中的當前項提供更多的信息。
import QtQuick 2.12 import QtQuick.Window 2.12 Window { visible: true width: 640 height: 480 title: qsTr("動畫與數據模型組合使用") Item { width: 300 height: 480 Rectangle { anchors.fill: parent gradient: Gradient { GradientStop { position: 0.0; color: "#4a4a4a" } GradientStop { position: 1.0; color: "#2b2b2b" } } } //視圖 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: "#333" border.color: Qt.lighter(color, 1.2) Text { anchors.left: parent.left anchors.verticalCenter: parent.verticalCenter anchors.leftMargin: 4 font.pixelSize: parent.height-4 color: '#fff' text: name } } Rectangle { id: image width: 26 height: 26 anchors.right: parent.right anchors.top: parent.top anchors.rightMargin: 2 anchors.topMargin: 2 color: "black" 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 gradient: Gradient { GradientStop { position: 0.0; color: "#fed958" } GradientStop { position: 1.0; color: "#fecc2f" } } border.color: '#000000' border.width: 2 Text { anchors.fill: parent anchors.margins: 5 clip: true wrapMode: Text.WordWrap color: '#1f1f21' font.pixelSize: 12 text: facts } } } Rectangle { anchors.right: parent.right anchors.top: parent.top anchors.rightMargin: 2 anchors.topMargin: 2 width: 26 height: 26 color: "#157efb" border.color: Qt.lighter(color, 1.1) 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" } } ] } } } }
顯示效果如下圖所示:
到此這篇關於QML中動態與靜態模型應用詳解的文章就介紹到這瞭,更多相關QML模型內容請搜索WalkonNet以前的文章或繼續瀏覽下面的相關文章希望大傢以後多多支持WalkonNet!
推薦閱讀:
- Qt6基於Qml的文件對話框演示效果
- 教你如何使用qt quick-PathView實現好看的home界面
- 使用qt quick-ListView仿微信好友列表和聊天列表的示例代碼
- 利用上下文屬性將 C++ 對象嵌入 QML 裡
- C++數據模型應用在QML委托代理機制中