实用指南:Qt/QML ListView组件基础用法示例

QML 中的 ListView​ 是 Qt Quick 框架中用于展示线性滚动列表的核心组件,遵循经典的 ** 模型-视图-代理(Model-View-Delegate)**​ 模式。它继承自 Flickable,天生支持触摸/鼠标滚动、惯性滑动等交互,同时提供了丰富的内置功能(如分组、动态加载、动画过渡)和高度的可定制性。

本文示例,采用Qt5.15进行开发。

一、核心设计理念:模型-视图-代理(MVD)

ListView 的设计遵循 Qt 的 MVD 模式,将数据逻辑视图展示项渲染分离:

  • 模型(Model):负责提供数据(如列表项的内容、数量、分组信息)。
  • 视图(ListView):负责管理布局、滚动、交互等视图逻辑。
  • 代理(Delegate):负责将模型中的单条数据渲染为具体的 QML 组件(即列表项的外观)。

二、基础用法

1. 导入模块与基本结构

首先导入 Qt Quick 模块:

import QtQuick 2.15
import QtQuick.Window 2.15

创建一个简单的 ListView

Window {
    width: 400; height: 600
    visible: true
    // 1. 定义数据模型(ListModel)
    ListModel {
        id: myModel
        ListElement { text: "Item 1"; color: "lightblue" }
        ListElement { text: "Item 2"; color: "lightgreen" }
        ListElement { text: "Item 3"; color: "salmon" }
        // ... 更多项
    }
    // 2. 创建 ListView
    ListView {
        id: myList
        anchors.fill: parent
        anchors.margins: 10
        // 绑定模型
        model: myModel
        // 定义代理(每个列表项的渲染模板)
        delegate: Rectangle {
            // 关键:获取视图宽度,让项填满列表宽度
            width: ListView.view.width - 20
            height: 60
            radius: 8
            color: model.color // 访问模型的数据
            // 显示文本(访问模型的 text 属性)
            Text {
                text: model.text
                anchors.centerIn: parent
                font.pixelSize: 18
            }
            // 鼠标悬停效果
            MouseArea {
                anchors.fill: parent
                hoverEnabled: true
                onEntered: parent.color = "gray"
                onExited: parent.color = model.color
                onClicked: console.log("Clicked:", model.text)
            }
        }
        // 可选:添加滚动条
        ScrollBar.vertical: ScrollBar {
            policy: ScrollBar.AlwaysOn // 始终显示滚动条
        }
    }
}

三、关键组件详解

1. 数据模型(Model)

ListView 支持多种模型,从简单的 ListModel 到自定义的 C++ 模型(QAbstractItemModel 子类)。

(1)简单模型:ListModel

ListModel 是 Qt Quick 内置的轻量级模型,适合静态或少量动态数据:

ListModel {
    id: fruitModel
    ListElement { name: "Apple"; price: 5; icon: "apple.png" }
    ListElement { name: "Banana"; price: 3; icon: "banana.png" }
    ListElement { name: "Orange"; price: 4; icon: "orange.png" }
}
  • ListElement:定义单条数据的结构(属性名对应代理中的 model.xxx)。
(2)自定义模型:C++ QAbstractItemModel

对于大量数据或复杂逻辑(如数据库查询、网络加载),建议用 C++ 实现 QAbstractListModelQAbstractItemModel,再注册到 QML 中:

  • C++ 端:实现 rowCount()(返回项数)、data(const QModelIndex &index, int role)(返回指定角色数据)等方法。
  • QML 端:注册模型类型后使用:
    import com.example.models 1.0
    ListView {
        model: FruitModel {} // 自定义 C++ 模型
        delegate: ...
    }

2. 代理(Delegate)

代理是 ListView核心定制点,决定了每个列表项的外观和交互。代理是一个 QML 组件,会为模型的每一条数据生成一个实例。

(1)访问模型数据

代理通过以下方式访问模型数据:

  • modelData:通用属性,对应 ListModel 中的单条数据(或自定义模型的默认角色)。
  • model.roleName:访问自定义模型的特定角色(如 model.namemodel.price)。
  • index:当前项的索引(从 0 开始)。
(2)常用技巧
  • 动态样式:根据索引或数据改变外观(如奇偶行不同颜色):
    delegate: Rectangle {
        color: index % 2 === 0 ? "white" : "#f0f0f0"
        // ...
    }
  • 交互处理:通过 MouseAreaTapHandler 处理点击、长按等事件。
  • 性能优化:避免在代理中使用复杂计算或大量子项,减少重绘开销。

3. 视图属性与配置

ListView 提供了丰富的属性调整布局和行为:

属性/方法说明
orientation布局方向(默认 Qt.Vertical,可设为 Qt.Horizontal 水平列表)
spacing列表项之间的间距(默认 0)
clip是否裁剪超出视图的内容(默认 true,防止项溢出)
cacheBuffer预渲染的项数(如设为 100,会提前渲染当前可见项前后各 100 项,提升滚动流畅度)
boundsBehavior滚动到边界的行为(Flickable.StopAtBounds 停止,Flickable.Overshoot 允许越界)
currentIndexChanged当前选中项索引改变时触发的信号
currentIndex / currentItem获取/设置当前选中项的索引或组件实例
count列表项的总数(只读)

4. 高级功能

(1)分组(Sections)

通过 section 属性实现列表分组(如按首字母分组):

ListView {
    model: fruitModel
    // 分组依据:name 属性的首字母
    section.property: "name"
    section.criteria: ViewSection.FullString
    section.delegate: Rectangle {
        width: ListView.view.width
        height: 40
        color: "gray"
        Text {
            text: section // 分组标题(如 "A"、"B")
            anchors.centerIn: parent
            font.bold: true
        }
    }
}
(2)动态加载(Lazy Loading)

对于超长列表,用 cacheBuffer 预渲染 + 动态加载内容(如图片):

delegate: Rectangle {
    // ...
    Image {
        source: model.icon
        asynchronous: true // 异步加载图片
        placeholder: Rectangle { color: "lightgray" } // 加载中的占位符
    }
}
(3)动画过渡

为项的添加、删除、移动添加动画,提升用户体验:

ListView {
    // 添加项时的动画:淡入
    add: Transition {
        NumberAnimation { property: "opacity"; from: 0; to: 1; duration: 200 }
    }
    // 删除项时的动画:淡出
    remove: Transition {
        NumberAnimation { property: "opacity"; from: 1; to: 0; duration: 200 }
    }
}
(4)与其他组件联动
  • ScrollBar:自定义滚动条样式或行为:
    ScrollBar.vertical: ScrollBar {
        width: 8
        contentItem: Rectangle {
            radius: 4
            color: "gray"
            implicitWidth: 4
        }
    }
  • SectionScroller:快速跳转到分组:
    SectionScroller {
        listView: myList
        position: Qt.TopEdge
    }

四、信号与槽

ListView 提供了多个信号处理交互:

  • clicked(index, model):点击项时触发。
  • pressed(index, model):按下项时触发。
  • doubleClicked(index, model):双击项时触发。
  • movementEnded():滚动停止时触发。
  • currentIndexChanged(int index):当前选中项改变时触发。

示例:

ListView {
    // ...
    onClicked: (index, model) => {
        console.log("Clicked item:", index, model.text)
        currentIndex = index // 设置为当前选中项
    }
}

五、性能优化建议

  1. 1.减少代理复杂度:避免在代理中使用昂贵的计算、动画或大量子项。
  2. 2.合理设置 cacheBuffer:根据列表长度调整,避免内存浪费或滚动卡顿。
  3. 3.异步加载资源:图片、网络数据用 asynchronous 属性异步加载。
  4. 4.数据更新通知:自定义模型需正确触发 dataChangedrowsInserted 等信号,让 ListView 及时刷新。
  5. 5.避免频繁滚动:如需高频更新内容,可使用 Timer 节流。

总结

ListView 是 Qt Quick 中灵活、高效的线性列表组件,通过模型-视图-代理模式实现了数据与视图的分离。

惠州西湖

posted on 2025-12-08 10:05  ljbguanli  阅读(15)  评论(0)    收藏  举报