在实际项目中,有时候我们写好一个组件,但不是立即加载出来,而是触发某些条件后才动态的加载显示出来,当处理完某些操作后,再次将其关闭掉;
这样的需求,可以使用 Component 包裹着组件,然后使用 Loader 去动态的加载和关闭;
Component 可以加载任何部件,例如,Rectangle,Button,Image等等
如果需要包裹多个部件,使用 Item { } 包裹着这些部件即可;
Component {
Item {
Rectangle { }
Button { }
......
}
}
Component 有两个很常用信号,就是创建前触发和销毁前触发,类似构造函数和析构函数,也可以说时回调;
只需要在定义该槽函数即可
Window {
id: root
// 窗口创建时触发
Component.onCompleted: {
root.x = 200
root.y = 200
root.color = "green"
console.log("onCompleted", width, height)
}
// 窗口销毁时触发
Component.onDestruction: {
console.log("onDestruction")
}
}
窗口创建销毁前都会去执行相应方法
下面是使用Component加载一个 Rectangle
Component {
id: com
Rectangle {
id: rect
width: 200
height: 100
color: "red"
}
}
如果直接这样写上去,程序运行后,是不会在窗口中有加载Rectangle出来的,需要使用Loader 去动态加载才行
Loader {
id: loader
sourceComponent: com // 加载id为 com 的组件
}
在Loader中有一个status状态可以用于判断窗口处于哪个阶段;
填写上 onStatusChanged: { } 槽函数,在status状态发生改变时,此槽函数回立即触发;
status 一共有四种状态:
Loader.Null - 未加载 - 0
Loader.Ready - 已加载 - 1
Loader.Loading - 加载中 - 2
Loader.Error - 加载错误 - 3
可以在槽函数中,根据这些状态去做某些相应的操作:
Loader {
id: loader
// 异步加载部件,当这个部件很大时,例如加载很大的图片,或者加载很大的数据,为了不卡死界面,需要使用异步加载
asynchronous: true
sourceComponent: com
onStatusChanged: {
console.log("status:", status)
if (2 == status) {
// 处理一些操作
console.log("加载中...")
} else if (1 == status){
// 处理以下操作
console.log("加载完毕.")
}
}
}
需要注意的是,加载中,需要在异步加载窗口的时候才会体现出来,即属性 asynchronous: true
当然,也可以在被加载的Rectangle部件中,写上Component.onCompleted: { } 和 Component.onDestruction: { }
这样当Rectangle被成功加载后,或者被关闭后都会触发槽函数;
Rectangle {
id: rect
width: 200
height: 100
color: "red"
// 窗口创建时触发
Component.onCompleted: {
console.log("onCompleted", width, height)
}
// 窗口销毁时触发
Component.onDestruction: {
console.log("onDestruction")
}
}
那么,如何动态的去加载呢?
为了模仿这些场景,我们定义两个按钮,一个用于加载,一个用于关闭;
Button {
x: 250
text: "关闭"
onClicked: {
loader.sourceComponent = null
}
}
Button {
x: 250
y: 100
text: "显示"
onClicked: {
loader.sourceComponent = com
}
}
将sourceComponent 赋值null即可将窗口隐藏,将sourceComponent 赋值Component的id即可加载显示;
现在将Loader中的sourceComponent置null;
Loader {
id: loader
sourceComponent: null
......
}
当Rectangle被加载出来后,我们还能不能再修改他呢?
答案是可以的!
需要使用到loader.item属性;查看帮助文档介绍:
此属性保存当前加载的顶级对象。
那么也就是说,loader.item 也就相当于rect;即Rectangle的id;
新加第三个按钮,用于测试:
Button {
x: 250
y: 200
text: "修改属性"
onClicked: {
loader.item.width = 50
loader.item.height = 50
loader.item.color = "green"
}
}
另外 Loader的source属性可以加载自己编写的qml文件
Loader {
id: loader
source: "/MyRectangle.qml"
}
最后是代码分享:
import QtQuick 2.9
import QtQuick.Window 2.2
import QtQuick.Controls 2.2
Window {
id: root
visible: true
width: 640
height: 480
title: qsTr("Hello World")
color: "white"
minimumWidth: 300
minimumHeight: 400
maximumHeight: 500
maximumWidth: 400
// 透明度
//opacity: 0.5
// 窗口创建时触发
// Component.onCompleted: {
// root.x = 200
// root.y = 200
// root.color = "green"
// console.log("onCompleted", width, height)
// }
// // 窗口销毁时触发
// Component.onDestruction: {
// console.log("onDestruction")
// }
// Component 可以加载任何部件,然后可以使用 Loader 去动态加载,然后也可以再动态销毁掉
Component {
id: com
Rectangle {
id: rect
width: 200
height: 100
color: "red"
// 窗口创建时触发
Component.onCompleted: {
console.log("onCompleted", width, height)
}
// 窗口销毁时触发
Component.onDestruction: {
console.log("onDestruction")
}
}
// Component 内部如果需要添加多个部件,需要使用Item包裹着
// Item {
// id: item_id
// Rectangle {
// id: rect1
// width: 60
// height:60
// color: "yellow"
// }
// Button {
// id: btn
// y:100
// text: "测试按钮"
// }
// Image { }
// }
}
Loader {
id: loader
// 异步加载部件,当这个部件很大时,例如加载很大的图片,或者加载很大的数据,为了不卡死界面,需要使用异步加载
asynchronous: true
//source: "/MyRectangle.qml"
sourceComponent: com
onStatusChanged: {
console.log("status:", status)
// 加载中
if (2 == status) {
// 处理一些操作
console.log("加载中...")
} else if (1 == status){
// 处理以下操作
console.log("加载完毕.")
}
}
}
Button {
x: 250
text: "关闭"
onClicked: {
loader.sourceComponent = null
}
}
Button {
x: 250
y: 100
text: "显示"
onClicked: {
loader.sourceComponent = com
}
}
Button {
x: 250
y: 200
text: "修改属性"
onClicked: {
loader.item.width = 50
loader.item.height = 50
loader.item.color = "green"
}
}
}
完!