5.2 状态和过渡
通常,用户界面的某些部分可以在状态中描述。状态定义一组可以由特定条件触发的属性更改。可以有一个过渡连接着这些状态的改变,过渡定义了这些更改应该是如何激活的,或者应该应用任何其他的操作。当进入到一个状态时,动作(Actions)也可以被应用。
5.2.1 状态
在QML中使用状态(State)元素定义状态,它是需要绑定到任何 item 元素的状态数组(states)属性。状态通过名为 state 属性的值来标识,并且以元素的最简单的形式一系列属性变化组成。默认情况下 state 的值为空字符串。
Item {
id: root
states: [
State {
name: "go"
PropertyChanges { ... }
},
State {
name: "stop"
PropertyChanges { ... }
}
]
}
通过将一个新的已定义的状态名赋给带有状态的元素的 state 属性来改变状态。
** 注意: **
切换状态的另一种方法是使用状态元素的 when 属性。可以将 when 属性设置为一个表达式,该表达式求值为 true 时,该状态就会被应用。
Item {
id: root
states: [
...
]
Button {
id: goButton
...
onClicked: root.state = "go"
}
}
例如,交通灯可能有两个信号灯。上面的一个信号是红色的,而下一个信号的信号是绿色的。在这个例子中,两盏灯都不应该同时亮。让我们看一下状态图。
当系统被打开时,它会自动进入到作为默认状态的停止模式。停止状态将 light1 更改为红色和 light2 到黑色(off)。外部事件现在可以触发状态切换到 “go” 状态。在 “go” 状态中,我们将颜色属性从 light1 更改为黑色(off)和 light2 到绿色,以表明现在行人可以通过了。
为了实现这个场景,我们开始为2个灯光勾画用户界面。为了简单起见,我们使用了两个矩形(矩形的宽度和高度相同,这意味着它是一个正方形),设置 radius 属性的值为宽度的一半。
Rectangle {
id: light1
x: 25; y: 15
width: 100; height: width
radius: width/2
color: root.black
border.color: Qt.lighter(color, 1.1)
}
Rectangle {
id: light2
x: 25; y: 135
width: 100; height: width
radius: width/2
color: root.black
border.color: Qt.lighter(color, 1.1)
}
正如在状态图中定义的那样,我们想要有两个状态,一个是 “go” 状态,另一个是 “stop”状态,在这个状态中,每个状态都改变了红色或绿色的红绿灯。我们设置状态属性,以确保我们的交通灯的初始状态是 stop 状态。
** 注意: **
我们也可以通过设置一个 “go” 的状态来达到同样的效果,并且不提供显式的 “stop” 状态,即将 light1 的颜色设置为红色,并将 light2 的颜色设置为黑色。初始状态 “由初始属性值定义的状态” 将作为 “停止” 状态。
state: "stop"
states: [
State {
name: "stop"
PropertyChanges { target: light1; color: root.red }
PropertyChanges { target: light2; color: root.black }
},
State {
name: "go"
PropertyChanges { target: light1; color: root.black }
PropertyChanges { target: light2; color: root.green }
}
]
使用 PropertyChanges { target: light2; color: "black" } 在本例中并不是真正需要的,因为 light2 的初始颜色已经是黑色了。在一个状态中,只需要描述属性将如何从其默认状态(而不是之前的状态)更改。
我们使用鼠标区域来触发状态更改,该区域覆盖了整个红绿灯,并在单击时进行行走状态和停止状态之间的切换。
MouseArea {
anchors.fill: parent
onClicked: parent.state = (parent.state == "stop"? "go" : "stop")
}
我们现在能够成功地改变交通信号灯的状态。为了让UI更有吸引力,看起来更自然,我们应该添加一些动画效果的过渡。这些过渡可以由状态更改来触发。
** 注意: **
可以使用一个简单逻辑的脚本来替换 QML 状态。开发人员很容易落入这种陷阱:写的代码更像一个 JavaScript 程序而不是一个 QML 程序。
5.2.2 过渡
可以将一系列的过渡(或者叫转换)添加到每个项目中。过渡是由状态更改触发执行的。我们可以定义一个特定的过渡,可以使用 from: 和 to: 属性来确定应用哪个状态更改。这两个属性就像一个过滤器,当过滤器为真时,将应用转换。我们也可以使用 “” 来表示 “任意状态”。例如, from:""; to:"*",表示从任何状态到任何状态,这恰巧是 from 和 to 属性的默认值,这意味着转换会被被应用到每个状态的改变。
对于这个例子,我们希望在将状态从 “go” 切换到 “stop” 时,使颜色发生变化。对于另一个相反的状态更改(“stop”到“go”),我们希望保持颜色立即更改的效果,因此不需要应用转换。我们限制了使用 from 和 to 属性的转换,只过滤从 “go” 到 “stop” 的状态更改。在转换过程中,我们为每一盏灯添加两种颜色动画,这将使状态描述中定义的属性更改动态化。
transitions: [
Transition {
from: "stop"; to: "go"
// from: "*"; to: "*"
ColorAnimation { target: light1; properties: "color"; duration: 2000 }
ColorAnimation { target: light2; properties: "color"; duration: 2000 }
}
]
我们可以通过单击 UI 来更改状态。状态被立即应用,并且在转换运行时也会改变状态。因此,当状态处于从“stop”到“go”的转换时,尝试点击UI。你会看到这种变化会马上发生。
我们可以用这个 UI 来尝试新的功能,例如,把不活跃的光缩小,放大活跃的光。为此,我们需要添加另一个属性更改来扩展状态,并在转换过程中处理缩放属性的动画。另一种选择是添加一个 “注意” 状态,在其中添加闪烁黄色的灯光。为此,我们需要向转换中添加一个顺序动画,使其在一秒钟内转换为黄色(“to”动画的属性,一秒后变为黑色)。也许我们还想改变一下缓冲曲线,让这个例子看起来更有效果。
本章完