文档说我们不能在一个文件中使用相同的ID。这意味着我们可以在不同的文件中使用相同的 id,对吧?我不知道 QML 中 ID 的范围,所以我编写如下代码来测试它。
//a.qml
Item {
id: a_item
x:20;
y:b_item.x // cannot access this id
y:b1.x1 // can access
Item {
id:a1
x:20
Component.onCompleted : a1.x //this a1 is a.qml's a1 not the a1 in main.qml
}
}
//b.qml
Item {
id: b_item
x:20;
property int x1: 30;
}
//main.qml
Item {
a {
id:a1
Component.onCompleted : b1.x = 1 //can access
}
b {
id:b1
}
function() {
a_item.x = 1; // cannot access this id
}
}
我的问题:
1.
QML 中 ID 的范围是什么?在我的测试中,结果显示 Item 无法访问其孩子和他兄弟的 chilren 的 id,但可以访问他的父母或兄弟,对吧?
阿拉伯数字。
不同文件中的相同 ID只是我在我的代码中显示的,没有错误,我工作了。但是我怎样才能区分它们。
规范的答案是:
id
的作用域是组件作用域。
组件范围为:
QML 文档中的每个 QML 组件都定义一个逻辑作用域。每 文档至少有一个根组件,但也可以有其他根组件 内联子组件。组件作用域是对象的联合 组件中的 ID 和组件的根对象的属性。
这本身并不能过多地说明范围到底是什么以及如何充分利用它。信息量更大一点:
在 QML 中,组件实例将其组件作用域连接到 形成范围层次结构。组件实例可以直接访问 其祖先的组件范围。
基本上,qml 文件中的每个id
都像该源根项的属性一样实现。除了它不能通过someobj.someId
访问,只能通过someId
。
这意味着由于 qml 的动态作用域,从根对象延伸的分支中存在的任何对象都可以访问此 id。
只要它没有被同名的id
或property
所遮蔽。
a_item
将在a.qml
以及存在于其根Item
增长的分支中的任何对象中可见。
从main.qml
看不到它,因为该对象位于树的更下方,其中未定义a_item
。
在同样的思路中,可以从a.qml
访问b1
因为b1
是在实例化a.qml
main.qml
中定义的。但b_item
会 从那里看不到。
事实上,由于a1
和b1
是在main.qml
中定义的,是整个应用程序树的根,因此这两个id
从应用程序的每个对象中都是可见的,只要它是对象树的一部分,并且只要标识符没有被阴影化。请注意,它们在单一实例或无父对象中不可见,因为它们不是应用程序对象树的一部分。
obj tree a1 b1 a_item b_item
main.qml D D X X
a.qm V V D X
Item a1 V V V X
b.qml V V X D
D - defined here, V - visible here, X - not available
属性也是如此,尽管动态范围仅适用于为 qml 文件根元素定义的属性,这与id
不同,即使它们位于不同的子分支上也是可见的,这就是为什么在此答案的第一句话中我将其作为"实现该源根项的属性":
Obj
Obj
Obj
id: objid
property objprop
CustomObj
所以objid
将在CustomObj
中可见,但objprop
不会,因为它不是 id 并且未在根对象中定义。id
与执行此操作相同:
Obj
property objid : _objid
Obj
Obj
id: _objid
来自给定源的所有id
在 qml 源根对象的上下文中可见,随后在查找无法解析"更高"上下文中的标识符时,最终将下降到此上下文的其他所有内容。
最后,请记住微妙的陷阱 - 只有当您确定应用程序将在兼容的上下文树中实例化对象时,才能跨源使用id
s。
例如:
A.qml {
id: objA
B { } // objA will be visible to this object
}
main.qml
A {
B {} // objA will NOT be visible to this object
}
B {} // objA will NOT be visible to this object
陷阱继续 - 上下文树在对象树之前- 创建对象的上下文很重要,一旦设置就无法更改(根据上下文依赖关系对重父级设置某些限制)。
// ObjA.qml
Item {
id: objA
Component {
id: cm
ObjB {}
}
function create() { cm.createObject(objA) }
}
// ObjB.qml
Item {
Component.onCompleted: console.log(objA)
}
// main.qml
Component {
id: cm
Rect {}
}
Obj {
anchors.fill: parent
MouseArea {
anchors.fill: parent
acceptedButtons: Qt.LeftButton | Qt.RightButton
onClicked: {
if (mouse.button === Qt.LeftButton) {
cm.createObject(parent)
} else {
parent.create()
}
}
}
}
正如这个实际示例所示,即使在这两种情况下,新创建的对象都属于具有objA
标识符的同一对象的父级,但在main.qml
中创建的对象无法解析它,因为它是在尚未定义objA
的上下文中创建的,但如果对象是在上下文中创建的,它就可以工作objA
, 即使它被埋在树上更高的地方,它也会起作用。
用更通用的方式来说,id
在源的根对象的上下文中变得可见,并且在每个后续子上下文中保持可见,直到它被同名对象遮蔽。可见性无法向下到达树中定义id
的上下文之前存在的上下文。请注意细微的区别 -a_item
指的是Item
,而a1
指的是a
。而且由于a1
在a.qml
内部可见,因此它将始终引用main.qml
中的a
的一个实例,无论您可能处于a
的哪个实例中,而a_item
将为每个不同的a
实例引用不同的对象。a_item
是"相对的",在每个不同的a
实例中都会有所不同,但a1
是绝对的,并且将始终指a
的特定实例。之所以如此,是因为a1
是一个具体的实例,而a_item
是一个类型/原型。
// Obj.qml
Item {
id: obj
Component.onCompleted: console.log(obj === oid)
}
// main.qml
Obj { } // false
Obj { id: oid } // true
动态id
范围非常有用,可以减少实施解决方法以访问所需内容所需的时间。这也是为什么给id
描述性名称而不仅仅是main
是一个非常好的主意。
例如,如果您有一个管理多个views
的manager
,每个中都包含多个objects
,您可以快速访问每个object
的相应view
,还可以访问管理器,而无需实现任何其他内容。经验法则是,manager
必须排在第一位,然后每个view
都应该在manager
的上下文中创建,不一定直接在其中,而是在其中,并且每个object
都应该在view
的上下文中创建。当然,要注意不要遮蔽事物。如果你打破了这个规则,事情将无法正确解决。
View.qml { id: view }
manager
view1
object // view is view1
view2
object // view is view2
view3
object // view is view3
当然,这仅在特定目的的设计中才有意义,因为您知道上下文树的一般结构会是什么样子。如果你正在制作几乎可以去任何地方的通用元素,你绝对不应该依赖于跨源访问id
,你应该通过属性、别名等来实现一个更通用的使用接口。