QML中ListView访问子组件
老规矩先上结论:已经创建出来的 delegate 在contentItem.children里,但这个下标不是 model 的 index
var children = listview.contentItem.children
前言
写QML项目难免经常要用到ListView,可是ListView却不像Repeater一样拥有itemAt(index)这样的方法,只有itemAt(real x, real y),需要通过坐标值来得到子组件,而作为可以滑动的列表,坐标还得实时计算,这也太蠢了,我明明只是想遍历当前这些子组件而已!查了很多资料终于找到了答案,用它的contentItem(基类QQuickItem)拿子节点,而不是在ListView自己的接口里找
var children = listview.contentItem.children
举个简单的例子
ListView {
id: listview
model: 10
delegate: TextField {
function getText() {
return text;
}
}
function getAllText() {
var allText = [];
var children = listview.contentItem.children;
for (var i = 0; i != children.length; ++i) {
if (typeof children[i].getText !== "function") {
continue;
}
allText.push(children[i].getText());
}
return allText;
}
}
要注意的一些问题
children[i]不能当成第 i 条 model。ListView只会创建可见区域加上cacheBuffer里的 delegate,滑出去的会被销毁或者复用,所以这里遍历不到完整列表。contentItem下面也不只有 delegate,highlight、header、footer 都会混在里面,直接children[i].getText()会踩到没有这个函数的对象。
spacing也不会往 children 里插Item,它只是两项之间空出的距离。之前把下标对不上理解成 spacing 塞了占位节点,这个是错的。
利用model的方法
2018-07-07更新
如果要的是 model 里的数据,而不是 delegate 上的函数,就不要走children。for (var i in listview.model)拿到的i是键,i["xxx"]并不是某一行;整数 model 也没有字段可读。ListModel用get:
for (var i = 0; i < listview.count; ++i) {
allText.push(listview.model.get(i).xxx);
}
好久没更新了,一般不使用文中的方式,挺笨的。建议定义一个
property var []或ListModel或dict作为Listview的model使用,model的值变化则通过每个组件来修改,是预存好而不是惰性获取,参考示例如下