QML中神秘的Component
QML 中神秘的 Component
Component 内部定义的 qmltype,其实相当于类定义,没有实例化,所以外部⽆法直接访问,看起来像是闭包,外部⽆法访问,但是 Component 内部可以访问外部变量。
在 ListView 中有个代理模式,其中最为重要的是名为 delegate 的属性。
通过设置 delegate 以不同的⽅式,展⽰数据项。你能发现 ListView::delegate 的接⼝类型为 Component,却不是 Item。
下⾯通过⼀些代码为你⼀⼀道出 Component 不为⼈知的秘密
Component 包裹对象的作⽤域
这⾥的作⽤域亦可是可被访问的范围。
import QtQuick 2.0
Item {
//! [0]
id: root
Component {
//! [1]
id: com
Text {
//! [2]
id: label
text: "I am Text"
//! [2]
}
/
/! [1]
}
Loader {
anchors.fill: parent
sourceComponent: com
}
//! [0]
}
如上诉代码,其中有三个可访问的标识符:root,com,label。root 在整个 0 是可以被访问的,com 访问域也是在整个 0 区。但是label 只能在 2 区被访问。刚好被 1 给包裹了。
其实,label 就是作为 com 的模板对象(模板图元),可以在后续使⽤中,⽤来构造对应的对象。
Component 和 Loader 的配合
讲完之后。我们再来⼀次代码看⼀看。⾸先我们定义⼀个 Panel,如下:
//~ Panel.qml
import QtQuick 2.0
Rectangle {
width: 100
height: 100
color: "transparent"
border.width: 1
property Component label
Loader {
id: loader
anchors.fill: parent
anchors.margins: parent.width * 0.1
sourceComponent: label
}
}
这⾥的 Loader 会从 label 那⾥获取到⼀个模板图元,然后依据这个模板图元进⾏对象构建, Loader 作为⽗亲参数调⽤ label 的 object Component::createObject(Item parent, object properties) 函数,返回的对象设置给 Loader::item 属性。
当 Loader::sourceComponent 改变的时候,他会先销毁之前的根据 label 构建的对象,然后再重新构建新对象,设置到 Loader::item。
下⾯是 Loader::setSourceComponent(Component c) 的 c++ 伪码:
void Loader::setSourceComponent(Component c)
{
if(this.sourceComponent != c) {
if(this.item != null) {
this.item.deleteLater();
this.item = null;
}
this.sourceComponent = c;
emit this.sourceComponentChanged(); // 信号触发后,就构建新的对象到 this.item
}
}
Compoent 的⾃动装箱
测试代码如下:
import QtQuick 2.5
Item {
width: 640
height: 360
Row {
anchors.fill: parent
Panel {
id: panel1
label: Component {
id: greenCom
Rectangle {
color: "green"
console.log("Green Rectangle Destruction")
}
}
}
}
Panel {
id: panel2
label: Rectangle { color: "red" }
}
}
Component {
id: yellowCom
Rectangle {
color: "yellow"
}
}
MouseArea {
anchors.fill: parent
onClicked: {
panel1.label = yellowCom;
if(time == 0) {
console.log("set cache only once")
cache = panel2.label;
}
panel2.label = yellowCom;
++time;
console.log(cache, cache.progress)
}
}
property int time: 0
qt listviewproperty var cache
console.log(panel2.label)
}
}
其中 panel2 的 lable 参数不是 Component,但是 Qt 会为其构建⼀个 Component 进⾏⾃动封装(不是类型转换,类似 Java 的装箱)。当你修改 panel2 的 lable 属性时,跟前⾯所说的⼀样,旧的对象会被销毁,并被新的对象替代。同理,panel1 也是。
因为⾃动装箱⽽产⽣的 Component 回收情况
你会注意到,panel2 之前⾃动产⽣ label 对象保存在 cache 中,并没有被销毁(访问 progress 不报空指针异常)。也就是装箱产⽣的封装类,不会被马上回收。
由于是 Qt ⾃动⽣成的,所以其对象所有权应该属于 QmlEngine,在满⾜⼀些条件时会被⾃动回收。可以尝试使⽤ ObjectOwnership QQmlEngine::objectOwnership(QObject * object) 查询对象的所有权,这⾥没有进⾏测试,有空再说啦~
版权声明:本站内容均来自互联网,仅供演示用,请勿用于商业和其他非法用途。如果侵犯了您的权益请与我们联系QQ:729038198,我们将在24小时内删除。
发表评论