QtQuick 中的模型视图代理
//对于开发用户界面,最重要的一方面是保持数据与可视化的分离。
//在QML中,model与view通过delegate连接起来。model提供数据,对于每个数据项,可能有多个值;显示在view中
//的每项数据都是通过代理来实现可视化;view的任务是排列这些delegate.
* 最基本的分离数据与显示的方法是使用Repeater元素,它被用于实例化一组元素项,并且很容易与一个用于填充用户
界面的定位器相结合。
//实现举例,repeater元素用于实现子元素的标号
import QtQuick 2.0
Column{
spacing: 2
Repeater{
model: 10
Rectangle{
width: 100
height: 20
radius: 3
color: "lightBlue"
Text{
anchors.centerIn: parent
text: index
}
}
}
}
//运行效果:
//借助JavaScript中的序列:
import QtQuick 2.0
Column{
spacing: 2
Repeater{
model: ["Enterprise","Colombia","Challenger","Discovery","Endeavour","Atlantis"];
Rectangle{
width: 100
height: 20
radius: 3
color: "lightBlue"
Text{
anchors.centerIn: parent
text: index + ": " + modelData
}
}
}
}
//运行效果:
//每个元素都提供了一个名字和一个元素
import QtQuick 2.0
Column{
spacing: 2
Repeater{
model: ListModel{
ListElement{ name: "Mercury"; surfaceColor: "gray" }
ListElement{ name: "Venus"; surfaceColor: "yellow" }
ListElement{ name: "Earth"; surfaceColor: "blue" }
ListElement{ name: "Mars"; surfaceColor: "orange" }
ListElement{ name: "Jupiter"; surfaceColor: "orange" }
ListElement{ name: "Saturn"; surfaceColor: "yellow" }
ListElement{ name: "Uranus"; surfaceColor: "lightBlue" }
ListElement{ name: "Neptune"; surfaceColor: "lightBlue" }
}
Rectangle{
width: 100
height: 20
radius: 3
color: "lightBlue"
Text{
anchors.centerIn: parent
text: name
}
Rectangle{ //圆
anchors.left: parent.left
anchors.verticalCenter: parent.verticalCenter
anchors.leftMargin: 2
width: 16
height: 16
radius: 8
border.color: "black"
border.width: 1
color: surfaceColor
}
}
}
//运行效果:
import QtQuick 2.0
Column{
spacing: 2
Repeater{
model: 10
Rectangle{
width: 100
height: 20
radius: 3
color: "lightBlue"
Text{
anchors.centerIn: parent
text: index
}
}
}
}
//repeater的内容的每个子项实例化时绑定了默认的属性delegate,运行效果同第一段代码
/*****************************************************************************************************/
动态视图:
//Repeater元素适合有限的静态数据,但在真正使用时,模型通常复杂庞大。QtQuick提供了ListView和GridView元素,
//这两个都是基于可滑动区域的元素,用户可放入更大的数据。
//链表视图:
import QtQuick 2.0
Rectangle{
width: 80
height: 300
color: "white"
ListView{
anchors.fill: parent
anchors.margins: 20
clip: true; //显示超过parent尺寸时,不显示
model: 100
delegate: numberDelegate
spacing: 5
}
Component{
id: numberDelegate
rectangle{
width: 40
height: 40
color: "lightGreen"
Text{
anchors.centerIn: parent
font.pixelSize: 10
text: index
}
}
}
}
//视图末尾的行为是由boundsBehavior属性控制的,这是一个枚举值。Flickable.DragAndOvershootBounds默认模式下,
//内容到达最后会反弹回去;视图可通过边界线来拖拽和翻阅:Flickable.StopAtBounds,视图将不可移到边界线外;
//可以将视图拖拽到它的边界线外,但在边界线上翻阅将无效:Flickable.DragOverBounds
使用snapMode属性可以限制一个视图内元素的停止位置。默认是ListView.NoSnap,允许视图内元素在任何位置停止。
ListView.SnapToItem:视图顶部将会与元素对象的顶部对齐排列。ListView.SnapOneItem:当鼠标或触摸释放时,视图会停止在第一个可见元素
/********************************************************************************************************************************************/
方向:
//默认的链表视图只提供了一个垂直方向的滚动条,链表视图的方向由orientation控制,ListView.Vertical,ListView.Horizontal
//水平链表简例:
import QtQuick 2.0
Rectangle{
width: 480
height: 80
color: "white"
ListView{
anchors.fill: parent
anchors.margins: 20
clip: true
model: 100
orientation: ListView.Horizontal
delegate: numberDalegate
spacing: 5
}
Component{
id: numberDelegate
Rectangle{
width: 40
height: 40
color: "lightGreen"
Text{
anchors.centerIn: parent
font.pixelSize: 10
text.index
}
}
}
}
//可以通过设置layoutDirection属性来控制元素顺序方向,可以被设置为Qt.LeftToRight 或 Qt.RightToLeft
/*****************************************************************************************************/
键盘导航和高亮:
当使用基于触摸方式的链表视图时,默认提供的视图已经足够使用。在使用键盘选择一个元素时,需要有标识当前选中元素
的机制。在QML中,这被叫做高亮。
import QtQuick 2.0
Rectangle{
width: 240
height: 300
color: "white"
ListView{
anchors.fill: parent
anchors.margins: 20
clip: true
model: 100
delegate: numberDelegate
spacing: 5
highlight: highlightComponent //指出使用的高亮代理元素
focus: true
}
Component{
id: hightlightComponent
Rectangle{
width: ListView.view.width
color: "lightGreen"
}
}
Component{
id: numberDelegate
Item{
width: 40
height: 40
Text{
anchors.centerIn: parent
font.pixelSize: 10
text:index
}
}
}
}
//运行结果:
highlightRangeMode控制了高亮如何影响视图中当前的显示:
默认设置ListView.NoHightlightRange意味着高亮与视图中元素的距离不相关
ListView.StrictlyEnforceRange确保了高亮始终可见
ListView.ApplyRange在需要的情况下高亮代理允许移出当前视图
默认配置下,视图负责高亮移动到指定位置,移动的速度和事件能够被改变:
highlightMoveSpeed, highlightMoveDuration,
highlightResizeSpeed, highlightResizeDuration
默认速度:400像素,动作持续时间-1,若速度和持续时间都被设置,采用速度快的完成
为了更加详细的控制高亮的移动,highlightFollowCurrentItem属性设置为false,表示视图
不再负责高亮代理的移动,取而代之的是通过一个Behavior或者一个动画来控制它。
/* 下面例子中,高亮代理的y坐标属性与ListView.view.currentItem.y属性绑定,确保了高亮始终跟随当前元素。
然而,我们没有让视图来移动这个高亮代理,所以我们需控制这个元素如何移动,通过Behavior on y完成 */
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: 1 } //y发生变化,更该当前项的y值
PropertyAnimation{
target: highlightRectangle
property: "opacity"
to: 1; duration: 200
}
}
}
Rectangle{
id: highlightRectangle
anchors.fill: parent
color: "lightGreen"
}
}
}
/*****************************************************************************************************/
页眉和页脚:
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
}
}
}
}
//页眉页脚不遵循间隔属性,它们被直接放在链表元素之上或之下。页眉页脚的间隔必须通过页眉页脚元素自己设置
/************************************************************************************************************************/
网格视图:
网格视图(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.pixSize: 10
text: index
}
}
}
}
GridView也包含了页眉和页脚,也可以使用高亮代理并支持捕捉模式(snap)。它也可以使用不同的方向(orientation)
与定向(direction)来定位。
定向使用flow属性来控制,可被设置为GridView.LeftToRight, GridView.TopToBottom
layoutDirection属性和flow属性能够适配网络从左到右或者从右到左
/***********************************************************************************************************/
代理(Delegate)
当使用模型与视图来自定义用户界面时,代理在创建显示时扮演了大量的角色。模型中的每个元素通过代理来实现
可视化,用户真实可见的是这些代理元素。
每个代理访问的索引号或者绑定的属性,一些是来自数据模型,一些来自视图。来自数据的数据将会通过属性传递到代理,
来自视图的数据将会通过属性传递视图中与代理相关的状态信息。
通常使用的视图绑定属性是ListView.isCurrentItem和ListView.view.通过访问视图,可以创建可复用的代理,这些
代理在被包含时会自动匹配视图的大小。
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信号。
/*******************************************************************************************************/
动画添加与移除元素:
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
}
Componment{ //点击代理时,移除一个元素
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
}
}
GridView.onAdd: SequentialAnimation{
NumberAnimation{
NumberAnimation{
target: wrapper
property: "scale"
from: 0; to: 1; duration:250
easing.type = ...
}
}
}
}
}
//形变的代理,路径视图,性能协调等知识就先不写了,写够了,路径视图等爱动弹时再单独补充吧
//本文主要翻译自英文文档。。。