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

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 整数作为模型

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

演示代码

qml 复制代码
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 数组

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

演示代码

qml 复制代码
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 字符串数组

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

演示代码

qml 复制代码
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 收到的相当于"模型换了",所有委托会推倒重建,委托里的临时状态(输入框里没提交的文字、勾选中的状态)会跟着丢。要频繁增删又不想丢状态,就得换成能按行增量增删的结构化模型。


已验证环境

相关推荐
江湖人称菠萝包8 小时前
【Qt】《Qt 5.9 C++开发指南》笔记-Chapter3-Qt类库概述
笔记·qt·qt5
qq_344920569 小时前
Qt事件过滤器
开发语言·c++·qt
sunriver20009 小时前
【Qt】由于找不到 Qt6Core5Compatd.dll
qt·qt6core5compatd
qq_344920569 小时前
Qt 鼠标双击QLabel全屏显示图像
c++·qt
江湖人称菠萝包12 小时前
【Qt】《Qt 5.9 C++开发指南》笔记-Chapter4-常用界面设计组件
笔记·qt·qt5
实心儿儿12 小时前
Qt — QtCreator 创建项目
qt
Quz1 天前
QML 常见的四种导航布局
qt
江湖人称菠萝包1 天前
【Qt】《Qt 5.9 C++开发指南》笔记-Chapter2-GUI应用程序设计基础
笔记·qt·qt5
重生的黑客1 天前
Qt 常用控件精讲(1):QWidget 核心属性全解 —— 从 geometry 到 qrc 与 QSS
c++·qt·qwidget·qss·常用控件