一、理解qml模型和视图
qt的发展是迅速的,虽然在每一个release版本中或多或少都有bug,但是作为一个庞大的gui库,no,应该说是一个开发框架开说,qt已经算是做的相当好。qml部分是qt4.7的时候推出的,当时qml只是为了移动端而设计的开发语言,随着it行业的发展,桌面端也产生了一定的需求,这就使得qml也必须支持桌面端的开发。使用qml可以做出绚丽的界面,并把逻辑和界面展示分开,qml和C++就好比html和JavaScript。
qt中有大量的model/view类,视图类:QTableView、QListView和QTreeView,模型类:QAbstractTableModel、QAbstractListModel和QAbstractProxyModel,这三个模型类都是继承自QAbstractItemModel。对于qml语言来说,也有model和view,他们分别就是GridView、ListView和PathView,其中GridView和ListView在qt中都有对于的实现类,PathView是最难理解的view,也是最灵活的view,他可以做出各种各样比较绚丽的效果。
二、效果展示
这一节我们主要是说明qml中的视图类,如下效果图展示的那样,图1是用GridView实现,图2是用ListView实现,view的item项都是用代理进行绘制;图3是用PathView实现的一个效果图,类似于一个卡牌弹出弹入效果。
顺道提一嘴,qml文件都是可以使用qmlscene.exe来预览
图1 gridview效果图
图2 listview效果图
图3 pathview效果图
三、源码分析
每一个示例中都有大量的注释,具体细节可以参看注释
1、GridView增删
如图1所示,这是一个GridView的简单示例,示例完成单击Add Item按钮实现新增项,点击项实现删除等功能。
1 import QtQuick 2.0 2 3 Rectangle { 4 480; 5 height: 300; 6 7 //背景色渐变 8 gradient: Gradient { 9 GradientStop { position: 0.0; color: "#dbddde"; } 10 GradientStop { position: 1.0; color: "#5fc9f8"; } 11 } 12 13 //list模型默认9项 14 ListModel { 15 id: theModel 16 17 ListElement { number: 0; } 18 ListElement { number: 1; } 19 ListElement { number: 2; } 20 ListElement { number: 3; } 21 ListElement { number: 4; } 22 ListElement { number: 5; } 23 ListElement { number: 6; } 24 ListElement { number: 7; } 25 ListElement { number: 8; } 26 ListElement { number: 9; } 27 } 28 29 //Add Item按钮 30 Rectangle { 31 anchors.left: parent.left; 32 anchors.right: parent.right; 33 anchors.bottom: parent.bottom; 34 anchors.margins: 20; 35 36 height: 40; 37 38 color: "#53d769"; 39 border.color: Qt.lighter(color, 1.1); 40 41 Text { 42 anchors.centerIn: parent; 43 44 text: "Add item!"; 45 } 46 47 //点击时新增项 实现model的动态新增 48 MouseArea { 49 anchors.fill: parent; 50 51 onClicked: { 52 theModel.append({"number": ++parent.count}); 53 } 54 } 55 56 property int count: 9;// 57 } 58 59 GridView { 60 anchors.fill: parent; 61 anchors.margins: 20; 62 anchors.bottomMargin: 80; 63 64 clip: true; 65 66 model: theModel;//绑定数据源 67 68 cellWidth: 45;//设置项大小 69 cellHeight: 45; 70 71 delegate: numberDelegate;//设置绘制代理 72 } 73 74 //自定义绘制代理 75 Component { 76 id: numberDelegate; 77 78 Rectangle { 79 id: wrapper; 80 81 40; 82 height: 40; 83 84 //首先是一个渐变的矩形框 85 gradient: Gradient { 86 GradientStop { position: 0.0; color: "#f8306a"; } 87 GradientStop { position: 1.0; color: "#fb5b40"; } 88 } 89 90 //文本值是number的数值 91 Text { 92 anchors.centerIn: parent; 93 94 font.pixelSize: 10; 95 96 text: number; 97 } 98 99 //鼠标点击代理时,移除点击项 100 MouseArea { 101 anchors.fill: parent; 102 103 onClicked: { 104 if (!wrapper.GridView.delayRemove)//是否延迟移除 105 { 106 theModel.remove(index); 107 } 108 } 109 } 110 111 //GridView移除项 顺序动画 112 GridView.onRemove: SequentialAnimation { 113 //属性变化 114 PropertyAction { 115 target: wrapper; 116 property: "GridView.delayRemove"; 117 value: true; 118 } 119 //数字动画 120 NumberAnimation { 121 target: wrapper;//目标对象 122 property: "scale";//执行动画的属性 123 to: 0;//结束值 124 duration: 250;//动画持续时长 125 easing.type: Easing.InOutQuad;//动画执行曲线 126 } 127 PropertyAction { 128 target: wrapper; 129 property: "GridView.delayRemove"; 130 value: false; 131 } 132 } 133 134 //GridView新增项 顺序动画 135 GridView.onAdd: SequentialAnimation { 136 NumberAnimation { 137 target: wrapper; 138 property: "scale"; 139 from: 0;//开始值 140 to: 1; 141 duration: 250; 142 easing.type: Easing.InOutQuad; 143 } 144 } 145 } 146 } 147 }
2、列表
如图2所示,是一个使用ListView实现的列表控件,点击列表控件中的项,可以实现最大化来展示列表的详细信息
1 import QtQuick 2.0 2 3 Item { 4 300; 5 height: 480; 6 7 //渐变别景色 8 Rectangle { 9 anchors.fill: parent; 10 gradient: Gradient { 11 GradientStop { position: 0.0; color: "#4a4a4a"; } 12 GradientStop { position: 1.0; color: "#2b2b2b"; } 13 } 14 } 15 16 //主界面列表视图 17 ListView { 18 id: listView; 19 20 anchors.fill: parent; 21 22 delegate: detailsDelegate;//设置绘制代理 23 model: planets;//绑定数据源 24 } 25 26 ListModel { 27 id: planets; 28 29 ListElement { 30 name: "Mercury"; 31 imageSource: "images/mercury.jpeg"; 32 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." ; 33 } 34 ListElement { 35 name: "Venus"; 36 imageSource: "images/venus.jpeg"; 37 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."; 38 } 39 ListElement { 40 name: "Earth"; 41 imageSource: "images/earth.jpeg"; 42 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'."; 43 } 44 ListElement { 45 name: "Mars"; 46 imageSource: "images/mars.jpeg"; 47 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."; 48 } 49 } 50 51 Component { 52 id: detailsDelegate; 53 54 Item { 55 id: wrapper; 56 57 listView.width; 58 height: 30; 59 60 //列表项文本 61 Rectangle { 62 anchors.left: parent.left; 63 anchors.right: parent.right; 64 anchors.top: parent.top; 65 66 height: 30; 67 68 color: "#333"; 69 border.color: Qt.lighter(color, 1.2); 70 Text { 71 anchors.left: parent.left; 72 anchors.verticalCenter: parent.verticalCenter; 73 anchors.leftMargin: 4; 74 75 font.pixelSize: parent.height-4; 76 color: '#fff'; 77 78 text: name;//ListElement中的name 79 } 80 } 81 82 //列表项图标 83 Rectangle { 84 id: image; 85 86 26; 87 height: 26; 88 89 anchors.right: parent.right; 90 anchors.top: parent.top; 91 anchors.rightMargin: 2; 92 anchors.topMargin: 2; 93 94 color: "yellow"; 95 96 Image { 97 anchors.fill: parent; 98 99 fillMode: Image.PreserveAspectFit; 100 101 source: imageSource;//ListElement中的imageSource 102 } 103 } 104 105 //鼠标点击列表项 进行状态前切换, 106 MouseArea { 107 anchors.fill: parent; 108 onClicked: parent.state = "expanded";//切换到展开状态 109 } 110 111 //详情页展开时,文本详细信息 112 Item { 113 id: factsView; 114 115 anchors.top: image.bottom;//位于放大后的图标底部 116 anchors.left: parent.left; 117 anchors.right: parent.right; 118 anchors.bottom: parent.bottom; 119 120 opacity: 0;//默认透明不显示 当点击代理项时该属性会慢慢变得可见 121 122 Rectangle { 123 anchors.fill: parent; 124 125 gradient: Gradient { 126 GradientStop { position: 0.0; color: "#fed958"; } 127 GradientStop { position: 1.0; color: "#fecc2f"; } 128 } 129 border.color: '#000000'; 130 border. 2; 131 132 Text { 133 anchors.fill: parent; 134 anchors.margins: 5; 135 136 clip: true;//可剪切 137 wrapMode: Text.WordWrap;//文本支持换行 138 color: '#1f1f21'; 139 140 font.pixelSize: 12; 141 142 text: facts; 143 } 144 } 145 } 146 147 //项最大化时 右上角关闭按钮 148 Rectangle { 149 id: closeButton; 150 151 anchors.right: parent.right; 152 anchors.top: parent.top; 153 anchors.rightMargin: 2; 154 anchors.topMargin: 2; 155 156 26; 157 height: 26; 158 159 color: "#157efb"; 160 border.color: Qt.lighter(color, 1.1); 161 162 opacity: 0; 163 164 MouseArea { 165 anchors.fill: parent; 166 onClicked: wrapper.state = "";//点击恢复到默认状态 167 } 168 } 169 170 //自定义代理状态 171 states: [ 172 State { 173 name: "expanded"; 174 //在点击列表项后 各项属相变化 175 176 //代理高度铺满视图高度 177 PropertyChanges { target: wrapper; height: listView.height; } 178 //列表项的图标放大 179 PropertyChanges { 180 target: image; 181 listView.width; 182 height: listView.width; 183 anchors.rightMargin: 0; 184 anchors.topMargin: 30//距离顶部30像素 185 } 186 //文本详细信息可见 187 PropertyChanges { target: factsView; opacity: 1; } 188 //关闭按钮可见 189 PropertyChanges { target: closeButton; opacity: 1; } 190 //列表项视图 191 PropertyChanges { 192 target: wrapper.ListView.view; 193 contentY: wrapper.y; 194 interactive: false 195 } 196 } 197 ] 198 199 //项变化时 过程 200 transitions: [ 201 Transition { 202 NumberAnimation { 203 duration: 200; 204 properties: "height,width,anchors.rightMargin,anchors.topMargin,opacity,contentY"; 205 } 206 } 207 ] 208 } 209 } 210 }
3、卡牌效果
示例代码可以直接放在qml文件中使用qmlscene.exe来预览
1 import QtQuick 2.6 2 3 Rectangle { 4 id: root; 5 480; 6 height: 300; 7 8 PathView 9 { 10 anchors.fill: parent; 11 12 delegate: flipCardDelegate; 13 model: 100; 14 15 path: Path{ 16 startX: root.width / 2; 17 startY: 0 18 19 PathAttribute { name: "itemAngle"; value: -45.0; } 20 PathAttribute { name: "itemScale"; value: 0.5; } 21 PathAttribute { name: "itemZ"; value: 0; }//属性值附加到代理上面 22 PathLine { x: root.width/2; y: root.height*0.4; }//路径元素定义 23 PathPercent { value: 0.48; }//控制两个元素之间的路径所占百分比 24 PathLine { x: root.width/2; y: root.height*0.5; } 25 PathAttribute { name: "itemAngle"; value: 0.0; } 26 PathAttribute { name: "itemScale"; value: 1.0; } 27 PathAttribute { name: "itemZ"; value: 100 } 28 PathLine { x: root.width/2; y: root.height*0.6; } 29 PathPercent { value: 0.52; } 30 PathLine { x: root.width/2; y: root.height; } 31 PathAttribute { name: "itemAngle"; value: 45.0; } 32 PathAttribute { name: "itemScale"; value: 0.5; } 33 PathAttribute { name: "itemZ"; value: 0; } 34 } 35 36 pathItemCount: 17;//可见元素数目 37 38 preferredHighlightBegin: 0.5; 39 preferredHighlightEnd: 0.5; 40 } 41 Component{ 42 id: flipCardDelegate; 43 44 Rectangle{ 45 id: wrapper; 46 47 64; 48 height: 64; 49 antialiasing: true;//反锯齿 50 51 //代理背景色渐变 52 gradient: Gradient{ 53 GradientStop { position: 0.0; color: "#2ed5fa"; } 54 GradientStop { position: 1.0; color: "#2467ec"; } 55 } 56 57 visible: PathView.onPath;//在PathView上的项可见,不在视图上的项不可见 58 59 scale: PathView.itemScale;//缩放 60 z: PathView.itemZ;//z值 数值大的在上面 61 62 property variant rotX: PathView.itemAngle;//属性别名 主要是因为要在底下这个旋转过程中使用 63 64 //动画过程旋转 65 transform: Rotation { 66 axis { x: 1; y: 1; z: 1; }//绕x轴旋转 67 angle: wrapper.rotX;//旋转角度 68 origin { x: 32; y: 32; }//旋转基点 69 } 70 Text{ 71 anchors.horizontalCenter: parent.horizontalCenter; 72 anchors.verticalCenter: parent.verticalCenter; 73 text: index; 74 } 75 } 76 } 77 }