# 动态加载组件

动态加载QML不同部分的最简单方法是使用Loader元素。它用作正在加载的项的占位符。要被加载的项通过source属性或sourceComponent属性控制。前者从给定的URL加载项目,而后者是实例化一个Component

由于加载器用作正在加载的项目的占位符,因此其尺寸取决于项目的尺寸,反之亦然。如果Loader元素有尺寸,通过设置widthheight或通过锚定,加载的项将被赋予加载器的尺寸。如果Loader没有尺寸,则根据正在加载的项的尺寸调整大小。

下面描述的示例演示了如何使用Loader元素将两个独立的用户界面部分加载到同一空间中。这个想法是有一个可以是数字或模拟的速度表盘,如下图所示。表盘周围的代码不受当前加载的项的影响。

image

image

应用程序的第一步是声明一个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属性,可以在不同的时间监控不同的元素。

image

在上面显示的示例中,向用户呈现了一个由两个可点击区域组成的用户界面。 单击任一区域时,都会使用动画闪烁。 左侧区域的代码片段如下所示。 在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
}

由于Bindingtarget元素并不总是被设置,并且可能并不总是具有给定的属性,所以Binding元素的when属性可用于限制绑定处于活动状态的时机。 例如,它可以限制为用户界面中的特定模式。

Binding元素还带有delayed属性。 当此属性设置为true时,直到事件队列被清空前绑定不会传播到target。 在高负载情况下,这可以作为一种优化,因为中间值不会被推送到target中。

最后更新: 1/28/2022, 7:37:27 PM