# 动态加载组件
动态加载QML不同部分的最简单方法是使用Loader
元素。它用作正在加载的项的占位符。要被加载的项通过source
属性或sourceComponent
属性控制。前者从给定的URL加载项目,而后者是实例化一个Component
。
由于加载器用作正在加载的项目的占位符,因此其尺寸取决于项目的尺寸,反之亦然。如果Loader
元素有尺寸,通过设置width
和height
或通过锚定,加载的项将被赋予加载器的尺寸。如果Loader
没有尺寸,则根据正在加载的项的尺寸调整大小。
下面描述的示例演示了如何使用Loader
元素将两个独立的用户界面部分加载到同一空间中。这个想法是有一个可以是数字或模拟的速度表盘,如下图所示。表盘周围的代码不受当前加载的项的影响。
应用程序的第一步是声明一个Loader
元素。 请注意,source
属性被省略了。 这是因为source
取决于用户界面所处的状态。
Loader {
id: dialLoader
anchors.fill: parent
}
在dialLoader
的父级元素的states
属性中,一组PropertyChanges
元素根据不同的state
驱动加载不同的QML文件。 在这个例子中,source
属性恰好是一个相对文件路径,但它也可以是一个完整的URL,可以通过网络获取项目。
states: [
State {
name: "analog"
PropertyChanges { target: analogButton; color: "green"; }
PropertyChanges { target: dialLoader; source: "Analog.qml"; }
},
State {
name: "digital"
PropertyChanges { target: digitalButton; color: "green"; }
PropertyChanges { target: dialLoader; source: "Digital.qml"; }
}
]
为了使加载的项目活跃起来,必须将speed
属性绑定到root的speed
属性。 这不能通过直接绑定来完成,因为项目并不总是被加载并随着时间而改变。 相反,必须使用Binding
元素。 每次Loader
触发onLoaded
信号时,绑定的target
属性都会更改。
Loader {
id: dialLoader
anchors.left: parent.left
anchors.right: parent.right
anchors.top: parent.top
anchors.bottom: analogButton.top
onLoaded: {
binder.target = dialLoader.item;
}
}
Binding {
id: binder
property: "speed"
value: root.speed
}
当项被加载后,onLoaded
信号运行加载QML动作。 以类似的方式,正在加载的QML可以依赖于Component.onCompleted
信号。 该信号实际上可用于所有组件,无论它们是如何加载的。 例如,整个应用程序的根组件可以在加载整个用户界面时使用它来启动自身。
# 间接连接
动态创建QML元素时,无法使用用于静态设置的onSignalName
方法连接到信号。 相反,必须使用Connections
元素,它可以连接到target
元素的任意数量的信号。
设置Connections
元素的target
属性后,可以像往常一样连接信号,即使用onSignalName
方法。 然而,通过改变target
属性,可以在不同的时间监控不同的元素。
在上面显示的示例中,向用户呈现了一个由两个可点击区域组成的用户界面。 单击任一区域时,都会使用动画闪烁。 左侧区域的代码片段如下所示。 在MouseArea
中,leftClickedAnimation
被触发,导致该区域闪烁。
Rectangle {
id: leftRectangle
width: 290
height: 200
color: "green"
MouseArea {
id: leftMouseArea
anchors.fill: parent
onClicked: leftClickedAnimation.start()
}
Text {
anchors.centerIn: parent
font.pixelSize: 30
color: "white"
text: "Click me!"
}
}
除了两个可点击区域之外,还使用了一个Connections
元素。 当点击活动的区域(即Connections
元素的target
)时,会触发第三个动画。
Connections {
id: connections
function onClicked() { activeClickedAnimation.start() }
}
为了确定要定位哪个MouseArea
,定义了两个状态。 请注意,不能使用PropertyChanges
元素设置target
属性,因为它已经包含target
属性;相反,使用了StateChangeScript
。
states: [
State {
name: "left"
StateChangeScript {
script: connections.target = leftMouseArea
}
},
State {
name: "right"
StateChangeScript {
script: connections.target = rightMouseArea
}
}
]
在尝试示例时,值得注意的是,当使用多个信号处理器时,所有信号处理器都会被调用。 然而,它们的执行顺序是不确定的。
创建Connections
元素但不设置target
属性的情况下,属性的默认值是parent
。 这意味着它必须显式设置为null
,以避免在设置target
之前捕获来自parent
的信号。 这种行为确实可以用来创建基于Connections
元素的自定义信号处理器组件。 这样,对信号做出反应的代码就可以被封装和重用。
在下面的示例中,Flasher
组件可以放在任何MouseArea
中。 当被单击时,它会触发动画,导致父级闪烁。 在同一个MouseArea
中,也可以执行被触发的实际任务。 这把标准化的用户反馈(即闪烁)与实际的动作分开。
import QtQuick
Connections {
function onClicked() {
// Automatically targets the parent
}
}
要使用Flasher
,只需在每个MouseArea
中实例化一个Flasher
,就可以了。
import QtQuick
Item {
// A background flasher that flashes the background of any parent MouseArea
}
当使用Connections
元素监控多种类型的target
元素的信号时,有时会发现自己处于可用信号因目标而异的情况。 这会导致Connections
元素在丢失信号时输出运行时错误。 为避免这种情况,可以将ignoreUnknownSignal
属性设置为true
,这会忽略所有此类错误。
提示
通常,隐藏错误消息并不是一个好的想法。如果这样做,请确保在注释中记录这样做的原因。
# 间接绑定
就像不可能直接连接到动态创建元素的信号一样,也不可能在不使用桥接元素的情况下绑定动态创建元素的属性。 要绑定任何元素的属性,包括动态创建的元素,使用Binding
元素。
Binding
元素允许指定一个target
元素、一个要绑定的property
和一个value
来绑定它。 例如,通过使用Binding
元素,可以绑定动态加载的元素的属性。 这在本章的介绍性示例中得到了证明,如下所示。
Loader {
id: dialLoader
anchors.left: parent.left
anchors.right: parent.right
anchors.top: parent.top
anchors.bottom: analogButton.top
onLoaded: {
binder.target = dialLoader.item;
}
}
Binding {
id: binder
property: "speed"
value: root.speed
}
由于Binding
的target
元素并不总是被设置,并且可能并不总是具有给定的属性,所以Binding
元素的when
属性可用于限制绑定处于活动状态的时机。 例如,它可以限制为用户界面中的特定模式。
Binding
元素还带有delayed
属性。 当此属性设置为true
时,直到事件队列被清空前绑定不会传播到target
。 在高负载情况下,这可以作为一种优化,因为中间值不会被推送到target
中。