QML中ListView访问子组件

Author Avatar
MistEO 6月 12, 2018

老规矩先上结论:已经创建出来的 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);
}
以前的评论
MistEO 2020-04-04

好久没更新了,一般不使用文中的方式,挺笨的。建议定义一个property var []或ListModel或dict作为Listview的model使用,model的值变化则通过每个组件来修改,是预存好而不是惰性获取,参考示例如下

Listview {
    property var mymodel: ["apple", "orange", "banana"]
    model: mymodel
    delegate: Textfield {
        text: modelData
        onTextChanged: {
                // mymodel[index] = text; //无法成功赋值
                var tmp = mymodel;
                tmp[index] = text;
                mymodel = tmp;
        }
    }
    function getText() {
        return mymodel;
    }
    function setText(textarray) {
        mymodel = textarray;
    }
}