QML Repeater 基础用法:整数、JS 数组、字符串数组三种模型

文章来源声明: 原文作者:Quz; 来源站点:掘金; 原文链接:https://juejin.cn/post/7686688669903093810; 本文基于上述来源整理/加工,觅优补充点评,仅供技术学习交流。版权归原作者所有。
觅优短评

适合快速搭出条数固定或结构简单的列表;记住数组要整体赋值,频繁增删或需保留行状态时再换 ListModel。

`Repeater` 是 Qt Quick 里负责批量生成的组件。这篇介绍三种最轻量的模型写法,直接给`model`一个整数、一个 JavaScript 对象数组、或者一个字符串数组。

先认识 Repeater 的三个要点

model(模型,定义数据) —— 告诉 Repeater "要做几份、每份的数据是什么"。它接受的东西很杂,整数、JS 数组、字符串列表、ListModel、C++ 模型都可以,这篇只介绍前三种不需要数据结构的。

delegate(委托,定义外观) —— 每一份长什么样。Repeater 会为模型里每一项实例化一份委托,按顺序放进父项的 data 中。

委托里能直接用的两个名字 —— index 是当前第几项(从 0 开始),modelData 是这一项的数据。整数模型只有 index 有意义,数组模型主要靠 modelData 获取内容。

还有个细节:Repeater 自带一个只读属性 count,表示当前委托个数。所以自己定义的"数量"属性千万别叫 count,会被遮蔽。

委托本身有两种写法。量少又只有这一个地方用,就把 Rectangle 直接内联写在委托后面;结构稍微复杂、或者想换个外观复用,可以抽成独立的 .qml 组件文件,委托写成 ItemRect { ... } ,只把数据传进去。

Demo 1 整数作为模型

1.gif

model 直接写整数,界面上就出现对应的行数,每行显示 Item0、Item1 这样的文字,序号来自委托里的 index

演示代码

import QtQuick
import QtQuick.Layouts

FadeInAnimation {
    // 整数模型:改这个值,Repeater 就会增删委托实例
    // 注意不能命名为 count —— Repeater 自带只读 count(当前项数),会把它遮蔽
    property int modelCount: 4
    readonly property int maxCount: 8

    ColumnLayout {
        anchors.fill: parent
        anchors.margins: 20
        spacing: 12

        // ... 省略标题组件 TitleSeparator ...

        CountBar {
            info: "model: " + modelCount
            canIncrease: modelCount < maxCount
            canDecrease: modelCount > 0
            onIncreaseRequested: modelCount++
            onDecreaseRequested: modelCount--
        }

        Repeater {
            model: modelCount

            delegate: Rectangle {
                Layout.fillWidth: true
                Layout.preferredHeight: 25
                radius: 6
                color: "#f5f5f5"
                border.color: "#ccc"
                border.width: 1

                Text {
                    anchors.centerIn: parent
                    text: "Item" + index
                    color: "#333"
                    font.pointSize: 10
                    font.bold: true
                }
            }
        }

        Item { Layout.fillHeight: true }
    }
}

关键逻辑解析

整数模型是最省的写法:model: modelCount 是属性绑定而不是一次性快照,整数一变 Repeater 就自动补删委托,所以"增加 / 减少"只需改那个数,行数跟着变。委托里只拿得到 index,文字用 "Item" + index 拼。两个小坑:外层数量属性不能叫 count,Repeater 自带只读的 count(当前委托个数),同名会把它遮蔽;Repeater 自己不是可视元素、也不参与布局,它只把委托塞进父项的 data,所以委托直接写 Layout.fillWidth 这类附加属性、交给外层布局统一排版。

Demo 2 JavaScript 数组

2.gif

model 使用了对象数组,每个元素带名字、颜色、描述三个字段。

演示代码

import QtQuick
import QtQuick.Layouts

FadeInAnimation {
    property var itemList: [
        { name: "Item1", color: "#3498db", des: "modelData.des" },
        { name: "Item2", color: "#e74c3c", des: "modelData.des" },
        { name: "Item3", color: "#2ecc71", des: "modelData.des" },
        { name: "Item4", color: "#f39c12", des: "modelData.des" }
    ]

    readonly property var colors: ["#3498db", "#e74c3c", "#2ecc71", "#f39c12"]
    readonly property int maxCount: 8

    ColumnLayout {
        anchors.fill: parent
        anchors.margins: 20
        spacing: 12

        // ... 省略标题组件 TitleSeparator ...

        CountBar {
            info: "数组长度: " + itemList.length
            canIncrease: itemList.length < maxCount
            canDecrease: itemList.length > 0
            // var 属性只有「重新赋值」才会发变更通知,push / splice 原地改数组不会刷新
            onIncreaseRequested: {
                var next = itemList.length
                itemList = itemList.concat({
                    name: "Item" + (next + 1),
                    color: colors[next % colors.length],
                    des: "modelData.des"
                })
            }
            onDecreaseRequested: itemList = itemList.slice(0, -1)
        }

        Repeater {
            model: itemList

            delegate: ItemRect {
                itemColor: modelData.color
                itemText: modelData.name
                itemDes: modelData.des
            }
        }

        Item { Layout.fillHeight: true }
    }
}

关键逻辑解析

对象数组的字段通过 modelData.字段 取——modelData 就是当前那一项的对象,这是最轻量的结构化数据,不需要定义任何模型类。真正的重点在增删:itemListvar 属性,Qt 只在整个属性被重新赋值时才发变更通知,push / splice 原地改数组界面不会刷新,所以"增加"用 concat 拼出新数组、"减少"用 slice 切出新数组再赋回去。颜色用 colors[next % colors.length] 按下标循环取,加到第几项都不会取到空值。

Demo 3 字符串数组

3.gif

model 使用一个纯字符串数组,委托里 modelData 直接等于那个字符串,不需要使用字段名属性。

演示代码

import QtQuick
import QtQuick.Layouts

FadeInAnimation {
    // 字符串数组:元素本身就是 modelData
    property var items: ["Item1", "Item2", "Item3", "Item4"]
    readonly property int maxCount: 8

    ColumnLayout {
        anchors.fill: parent
        anchors.margins: 20
        spacing: 12

        // ... 省略标题组件 TitleSeparator ...

        CountBar {
            info: "数组长度: " + items.length
            canIncrease: items.length < maxCount
            canDecrease: items.length > 0
            // var 属性只有「重新赋值」才会发变更通知,push / pop 原地改数组不会刷新
            onIncreaseRequested: items = items.concat("Item" + (items.length + 1))
            onDecreaseRequested: items = items.slice(0, -1)
        }

        Repeater {
            model: items

            delegate: Rectangle {
                Layout.fillWidth: true
                Layout.preferredHeight: 25
                radius: 6
                color: "#f5f5f5"
                border.color: "#ccc"
                border.width: 1

                Text {
                    anchors.centerIn: parent
                    color: "#333"
                    font.pointSize: 10
                    font.bold: true
                    text: modelData
                }
            }
        }

        Item { Layout.fillHeight: true }
    }
}

关键逻辑解析

纯字符串数组时 modelData 就是字符串本身,委托里直接 text: modelData,比对象数组少一层字段访问。坑和上一个示例一样:增删必须用 concat / slice 造出新数组再赋回。行外观又回到最简单的 Rectangle——委托长什么样和模型没有绑定关系,模型只负责两件事:有几项、每项什么数据。

三种模型怎么选

维度整数JavaScript 对象数组字符串数组
`model` 写法`model: 4``model: [{ name: …, color: … }]``model: ["a", "b"]`
委托里取数据`index``modelData.字段``modelData`
增删怎么写改那个整数`concat` / `slice` 造新数组后赋值同左
能不能原地改不涉及不能,必须重新赋值不能,必须重新赋值
适合什么固定条数的占位块少量带字段的行数据纯文本列表

如何选择?

数据有没有字段:只是"要几行",就写整数;每行有名字、颜色这类字段,就上对象数组。数据会不会变:会变就必须造新数组赋回去,图省事而又需要频繁增删的,直接看 ListModel 或 C++ 模型那一类结构化模型。

数组模型还有个副作用要留意。因为每次改数据都是整份重新赋值,Repeater 收到的相当于"模型换了",所有委托会推倒重建,委托里的临时状态(输入框里没提交的文字、勾选中的状态)会跟着丢。要频繁增删又不想丢状态,就得换成能按行增量增删的结构化模型。


已验证环境