6.4 代理
当在自定义用户界面中使用模型和视图时,代理在创建外观中扮演着重要角色。当模型中的每个项目都通过代理进行可视化时,用户实际可以看到的是代理。
每个代理都可以访问许多附加属性,一些来自数据模型,另一些来自视图。从模型来看,属性将每个项目的数据传达给代理。从视图来看,属性传递与视图中的代理相关的状态信息。
从视图附加的最常用的属性是 ListView.isCurrentItem 和 ListView.view。第一个是一个布尔值,指示项目是否是当前项目,而后者是对实际视图的只读引用。通过访问视图,可以创建一般的可重复使用的代理,以适应其所包含的视图的大小和性质。 在下面的示例中,每个委托的宽度被绑定到视图的 width,而每个代理的背景 color 取决于附加的 ListView.isCurrentItem 属性。
import QtQuick 2.5
Rectangle {
width: 120
height: 300
gradient: Gradient {
GradientStop { position: 0.0; color: "#f6f6f6" }
GradientStop { position: 1.0; color: "#d7d7d7" }
}
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?"#157efb":"#53d769"
border.color: Qt.lighter(color, 1.1)
Text {
anchors.centerIn: parent
font.pixelSize: 10
text: index
}
}
}
}
delegates-basic
如果模型中的每个项目与一个操作相关联,例如,单击一个项目就会对其执行操作,该功能就是每个代理的一部分。这将分割视图之间的事件管理,该视图处理视图中项目之间的导航以及处理特定项目上的操作的代理。
最基本的方法是在每个委托中创建一个 MouseArea,并对 onClicked 信号执行操作。这在本章下一节的例子中有所体现。
6.4.1 动态添加和删除项目
在某些情况下,视图中显示的内容会随时间而变化。随着底层数据模型的改变,项目被添加和删除。在这些情况下,通常使用视觉特征使用户能感觉这些变化并帮助用户了解到底添加或删除了哪些数据是一个好主意。
方便的是,QML 视图将两个信号 onAdd 和 onRemove 附加到每个项目代理。通过将动画连接到这些信号,可以轻松创建必要的动画来帮助用户识别发生了什么事。
下面的示例通过使用动态填充的 ListModel 来演示此示例。屏幕底部显示一个用于添加新项目的按钮。点击后,使用追加方法将新项目添加到模型中。这将触发在视图中创建新的代理,并发出 GridView.onAdd 信号。 附加到信号上的 SequentialAnimation 使得项目通过动画的来设置代理的 scale 属性放大视图。
当视图中的代理单击时,通过调用 remove 方法将该项目从模型中删除。这会导致 GridView.onRemove 信号被发出,这会触发另一个 SequentialAnimation 动画。 但是,这一次,代理的销毁必须被推迟到动画完成。为此,使用 PropertyAction 元素将 GridView.delayRemove 属性设置为 true,之后设置为 false。 这样可以确保动画效果在代理项目被真正的移除之前完成。
import QtQuick 2.5
Rectangle {
width: 480
height: 300
gradient: Gradient {
GradientStop { position: 0.0; color: "#dbddde" }
GradientStop { position: 1.0; color: "#5fc9f8" }
}
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: "#53d769"
border.color: Qt.lighter(color, 1.1)
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
gradient: Gradient {
GradientStop { position: 0.0; color: "#f8306a" }
GradientStop { position: 1.0; color: "#fb5b40" }
}
Text {
anchors.centerIn: parent
font.pixelSize: 10
text: number
}
MouseArea {
anchors.fill: parent
onClicked: {
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 }
}
}
}
}
6.4.2 形变代理
列表的常用机制是当激活时扩展当前项目。这可以用于动态地使项目展开铺满屏幕以方便向用户展示新的界面部分,或者可以用于为给定列表中的当前项提供更多的展示信息的区域。
在下面的示例中,每个项目在单击时将其扩展到包含它的 ListView 的完整范围。 然后使用额外的间隔来添加更多信息。 用于控制这种情况的机制是一个状态,扩展状态(expanded)中每个项目代理可以输入,扩展项目的位置。在这种状态下,许多属性会被改变。
首先,wrapper 的高度设置为 ListView 的高度。 然后,缩略图图像被放大并向下移动以使其从其较小位置移动到其较大位置。除此之外,这两个隐藏的项目,通过改变元素的不透明度(opacity)来显示 factView 和 closeButton。最后,设置ListView。
设置 ListView 包括将视图中可见部分的顶部的 contentY 设置为代理的 y 值。另一个变化是将视图的交互式(interactive)属性设置为 false。这样可以防止视图滚动。 用户不能再滚动列表或更改当前项目。
当项目首先被点击时,它进入扩展状态,导致项目代理填充 ListView 和内容重新排列。当点击关闭按钮时,状态被清除,导致代理返回到之前的状态,并使 ListView 重新可用。
import QtQuick 2.5
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 {
id: closeButton
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"
}
}
]
}
}
}
delegates-expanding-small
delegates-expanding-large
这里展示的扩展代理以填补整个视图的技术可以用来使得一个项目代理移动的形状要小得多。例如,当浏览歌曲列表时,可以使当前项目稍大一点,以容纳关于该特定项目的更多信息。
上面的扩展代理示例中使用的星球的图片:
earth jupiter mars mercury venus
网友评论