QML用ListView实现带section的GridView

QML自带的GridView只能定义delegate,没有section,类似手机相册带时间分组标签的样式就没法做。最简单的方式就是组合ListView+GridView,或者ListView+Flow,但是嵌套View时,子级View一般是完全展开的,只显示该分组几行就得把该分组全部加载了,这样就没有了View在需要时才实例化Item的优势,所以最好还是在单层View实现最终效果。

QML的ListView支持section,可以自定义分组样式,所以可以通过ListView来实现带section的GridView。当然,你也可以直接修改GridView的C++源码给他加上section。

ListView实现GridView的效果无非就是把多行显示到一行。可以让ListView某一行撑高,其他行高度为0;也可以平均分配一行高度。因为delegate会被ListView控制位置,所以相对位置可以在内部嵌套然后设置偏移量,使之看起来在一行上。

本文完整代码:

https://github.com/gongjianbo/MyTestCode/tree/master/Qml/TestQml_20240205_SectionGrid

先实现一个不带section的GridView:

import QtQuick 2.15
import QtQuick.Controls 2.15// ListView 实现 GridView 效果
Rectangle {id: controlborder.color: "black"// 边距property int padding: 10// Item 间隔property int spacing: 10// Item 宽property int itemWidth: 300// Item 高property int itemHeight: 100// Delegate 宽property int delegateWidth: itemWidth + spacing// Delegate 高property int delegateHeight: itemHeight + spacing// 列数根据可视宽度和 Item 宽度计算property int columns: (list_view.width + spacing - padding) / delegateWidth < 1? 1: (list_view.width + spacing - padding) / delegateWidth// 套一层 Item clip 剪去 ListView 尾巴上多余的部分不显示出来Item {anchors.fill: parentanchors.margins: control.padding// 右侧留下滚动条位置,所以 columns 里 list_view.width 要减一个 paddinganchors.rightMargin: 0clip: trueListView {id: list_viewwidth: parent.width// 高度多一个 delegate 放置 footer,防止末尾的一行滑倒底部后隐藏// 多出来的一部分会被外部 Item clip 掉height: parent.height + control.delegateHeight + control.spacingflickableDirection: Flickable.HorizontalAndVerticalFlickboundsBehavior: Flickable.StopAtBoundsheaderPositioning: ListView.OverlayHeader// 底部多一个 footer 撑高可显示范围,防止末尾的一行滑倒底部后隐藏footerPositioning: ListView.OverlayFooterScrollBar.vertical: ScrollBar {// padding 加上 ListView 多出来的一部分bottomPadding: padding + (control.delegateHeight + control.spacing)// 常驻显示只是方便调试policy: ScrollBar.AlwaysOn}footer: Item {// 竖向的 ListView 宽度无所谓width: control.delegateWidth// 高度大于等于 delegate 高度才能保证显示height: control.delegateHeight}// 奇数方便测试model: 31delegate: Item {width: control.delegateWidth// 每行第一个 Item 有高度,后面的没高度,这样就能排列到一行// 因为 0 高度 Item 在末尾,超出范围 visible 就置为 false 了,所以才需要 footer 撑高多显示一行的内容// delegate 高度不一致会导致滚动条滚动时长度变化height: (model.index % control.columns === 0) ? control.delegateHeight : 0// 放置真正的内容Rectangle {// 根据列号计算 xx: (model.index % control.columns) * control.delegateWidth// 负高度就能和每行第一个的 y 一样y: (model.index % control.columns !== 0) ? -control.delegateHeight : 0width: control.itemWidthheight: control.itemHeightborder.color: "black"Text {anchors.centerIn: parent// 显示行号列号text: "(%1,%2)".arg(parseInt(model.index / control.columns)).arg(model.index % control.columns)}}}}}
}

如果要带section,就得每个分组有单独的index,这样才能计算分组内的行列号,需要我们自定义一个ListModel:

#pragma once
#include <QAbstractListModel>// 实际数据
struct DataInfo
{int value;// 本例用日期来分组QString date;
};// 分组信息,如 index
struct SectionInfo
{int index;
};class DataModel : public QAbstractListModel
{Q_OBJECT
private:enum ModelRole {ValueRole = Qt::UserRole, GroupNameRole, GroupIndexRole};
public:explicit DataModel(QObject *parent = nullptr);// Model 需要实现的必要接口int rowCount(const QModelIndex &parent = QModelIndex()) const override;QVariant data(const QModelIndex &index, int role = Qt::DisplayRole) const override;QHash<int, QByteArray> roleNames() const override;// 在头部添加一个数据Q_INVOKABLE void appendData(int value, const QString &date);// 根据 model.index 删除一个数据Q_INVOKABLE void removeData(int index);// 加点测试数据void test();private:QVector<DataInfo> datas;QVector<SectionInfo> inners;
};DataModel::DataModel(QObject *parent): QAbstractListModel(parent)
{test();
}int DataModel::rowCount(const QModelIndex &parent) const
{if (parent.isValid())return 0;return datas.size();
}QVariant DataModel::data(const QModelIndex &index, int role) const
{if (!index.isValid())return QVariant();auto &&item = datas.at(index.row());auto &&inner = inners.at(index.row());switch (role){case ValueRole: return item.value;case GroupNameRole: return item.date;case GroupIndexRole: return inner.index;}return QVariant();
}QHash<int, QByteArray> DataModel::roleNames() const
{static QHash<int, QByteArray> names{{ValueRole, "value"}, {GroupNameRole, "groupName"}, {GroupIndexRole, "groupIndex"}};return names;
}void DataModel::appendData(int value, const QString &date)
{// 先判断分组是否相同if (datas.isEmpty() || datas.first().date != date) {// 没有该组,新建一个分组DataInfo item;item.value = value;item.date = date;SectionInfo inner;inner.index = 0;beginInsertRows(QModelIndex(), 0, 0);datas.push_front(item);inners.push_front(inner);endInsertRows();} else {// 已有该组,插入并移动该组后面的 ItemDataInfo item;item.value = value;item.date = date;SectionInfo inner;inner.index = 0;beginInsertRows(QModelIndex(), 0, 0);datas.push_front(item);inners.push_front(inner);endInsertRows();// 刷新该组int update_count = 0;// 0 是新插入,1 是旧 0for (int i = 1; i < inners.size(); i++) {auto &&inner_i = inners[i];if (i > 1 && inner_i.index == 0)break;inner_i.index = i;update_count ++;}emit dataChanged(QAbstractListModel::index(1, 0), QAbstractListModel::index(1 + update_count, 0));}
}void DataModel::removeData(int index)
{if (index < 0 || index >= datas.size())return;beginRemoveRows(QModelIndex(), index, index);datas.removeAt(index);inners.removeAt(index);endRemoveRows();int update_count = 0;for (int i = index; i < inners.size(); i++) {auto &&inner_i = inners[i];if (inner_i.index == 0)break;inner_i.index -= 1;update_count ++;}if (update_count > 0) {emit dataChanged(QAbstractListModel::index(index, 0), QAbstractListModel::index(index + update_count, 0));}
}void DataModel::test()
{DataInfo item;SectionInfo inner;item.date = "2022.2.22";for (int i = 0; i < 11; i++){item.value = i + 1;datas.push_back(item);inner.index = i;inners.push_back(inner);}item.date = "2010.10.10";for (int i = 0; i < 21; i++){item.value = i + 1;datas.push_back(item);inner.index = i;inners.push_back(inner);}item.date = "1999.9.9";for (int i = 0; i < 31; i++){item.value = i + 1;datas.push_back(item);inner.index = i;inners.push_back(inner);}
}
import QtQuick 2.15
import QtQuick.Controls 2.15
import Test 1.0// ListView 实现带 section 分组的 GridView
Rectangle {id: controlborder.color: "black"// 边距property int padding: 10// Item 间隔property int spacing: 10// Item 宽property int itemWidth: 300// Item 高property int itemHeight: 100// Delegate 宽property int delegateWidth: itemWidth + spacing// Delegate 高property int delegateHeight: itemHeight + spacing// 列数根据可视宽度和 Item 宽度计算property int columns: (list_view.width + spacing - padding) / delegateWidth < 1? 1: (list_view.width + spacing - padding) / delegateWidth// 套一层 Item clip 剪去 ListView 尾巴上多余的部分不显示出来Item {anchors.fill: parentanchors.margins: control.padding// 右侧留下滚动条位置,所以 columns 里 list_view.width 要减一个 paddinganchors.rightMargin: 0clip: trueListView {id: list_viewwidth: parent.width// 高度多一个 delegate 放置 footer,防止末尾的一行滑倒底部后隐藏// 多出来的一部分会被外部 Item clip 掉height: parent.height + control.delegateHeight + control.spacingflickableDirection: Flickable.HorizontalAndVerticalFlickboundsBehavior: Flickable.StopAtBoundsheaderPositioning: ListView.OverlayHeader// 底部多一个 footer 撑高可显示范围,防止末尾的一行滑倒底部后隐藏footerPositioning: ListView.OverlayFooterScrollBar.vertical: ScrollBar {// padding 加上 ListView 多出来的一部分bottomPadding: padding + (control.delegateHeight + control.spacing)// 常驻显示只是方便调试policy: ScrollBar.AlwaysOn}footer: Item {// 竖向的 ListView 宽度无所谓width: control.delegateWidth// 高度大于等于 delegate 高度才能保证显示height: control.delegateHeight}model: DataModel {id: list_model}section {property: "groupName"criteria: ViewSection.FullStringdelegate: Item {width: list_view.width - control.paddingheight: 40Rectangle {width: parent.widthheight: parent.height - control.spacingcolor: "gray"Text {anchors.centerIn: parenttext: sectioncolor: "white"}}}labelPositioning: ViewSection.InlineLabels}delegate: Item {width: control.delegateWidth// 每行第一个 Item 有高度,后面的没高度,这样就能排列到一行// 因为 0 高度 Item 在末尾,超出范围 visible 就置为 false 了,所以才需要 footer 撑高多显示一行的内容// delegate 高度不一致会导致滚动条滚动时长度变化height: (model.groupIndex % control.columns === 0) ? control.delegateHeight : 0// 放置真正的内容Rectangle {// 根据列号计算 xx: (model.groupIndex % control.columns) * control.delegateWidth// 负高度就能和每行第一个的 y 一样y: (model.groupIndex % control.columns !== 0) ? -control.delegateHeight : 0width: control.itemWidthheight: control.itemHeightborder.color: "black"Text {anchors.centerIn: parent// 显示行号列号text: "(%1,%2) - %3".arg(parseInt(model.groupIndex / control.columns)).arg(model.groupIndex % control.columns).arg(model.value)}Column {x: 12anchors.verticalCenter: parent.verticalCenterspacing: 12Button {width: 100height: 30text: "append"onClicked: {list_model.appendData(model.value, "2222.2.22")}}Button {width: 100height: 30text: "remove"onClicked: {list_model.removeData(model.index)}}}}} // end delegate Item} // end ListView}
}

这里只是实现了一个简单的效果,很多细节还需要调整。

通过添加更多的属性和计算,也可以实现带section的FlowView,即Item的宽高不是固定大小,整体为流式布局。

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/news/669679.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

Mybatis中的sql-xml延迟加载机制

Mybatis中的sql-xml延迟加载机制 hi&#xff0c;我是阿昌&#xff0c;今天记录一下关于Mybatis中的sql-xml延迟加载机制 一、前言 首先mybatis技术本身就不多介绍&#xff0c;说延迟加载机制之前&#xff0c;那要先知道2个概念&#xff1a; 主查询对象关联对象 假设咱们现…

OpenCV 笔记(20):霍夫圆检测

1. 霍夫圆变换 霍夫圆变换(Hough Circle Transform)是一种数字图像处理中的特征提取技术&#xff0c;用于在图像中检测圆形。它将二维图像空间中一个圆转换为该圆半径、圆心横纵坐标所确定的三维参数空间中一个点的过程。因此&#xff0c;圆周上任意三点所确定的圆&#xff0c…

ElasticSearch查询语句用法

查询用法包括&#xff1a;match、match_phrase、multi_match、query_string、term 1.match 1.1 不同字段权重 如果需要为不同字段设置不同权重&#xff0c;可以考虑使用bool查询的should子句来组合多个match查询&#xff0c;并为每个match查询设置不同的权重 {"query&…

二叉树的详解

二叉树 【本节目标】 掌握树的基本概念掌握二叉树概念及特性掌握二叉树的基本操作完成二叉树相关的面试题练习 树型结构&#xff08;了解&#xff09; 概念 树是一种非线性的数据结构&#xff0c;它是由n&#xff08;n>0&#xff09;个有限结点组成一个具有层次关系的集合。…

JS-本地文件上传

由于不需要原上传文件的样式&#xff0c;所以自己书写了一个按钮触发文件上传&#xff0c;并将原本的样式隐藏 <!doctype html> <html><head><meta charset"utf-8"><title>文件传输</title> </head><body><inpu…

工业笔记本丨行业三防笔记本丨亿道加固笔记本定制丨极端温度优势

工业笔记本是专为在恶劣环境条件下工作而设计的高度耐用的计算机设备。与传统消费者级笔记本电脑相比&#xff0c;工业笔记本在极端温度下展现出了许多优势。本文将探讨工业笔记本在极端温度环境中的表现&#xff0c;并介绍其优势。 耐高温性能: 工业笔记本具有更高的耐高温性…

安卓动态链接库文件体积优化探索实践

背景介绍 应用安装包的体积影响着用户下载量、安装时长、用户磁盘占用量等多个方面&#xff0c;据Google Play统计&#xff0c;应用体积每增加6MB&#xff0c;安装的转化率将下降1%。 安装包的体积受诸多方面影响&#xff0c;针对dex、资源文件、so文件都有不同的优化策略&…

【极数系列】Flink集成KafkaSource 实时消费数据(10)

文章目录 01 引言02 连接器依赖2.1 kafka连接器依赖2.2 base基础依赖 03 连接器使用方法04 消息订阅4.1 主题订阅4.2 正则表达式订阅4.3 Partition 列分区订阅 05 消息解析06 起始消费位点07 有界 / 无界模式7.1 流式7.2 批式 08 其他属性8.1 KafkaSource 配置项&#xff08;1&…

如何训练自己的模型

无论数据类型或目标如何&#xff0c;用于训练和使用 AutoML 模型的工作流都是相同的&#xff1a; 准备训练数据。 我们需要将需要训练的数据准备为jsonl格式&#xff0c;这种格式的特点就是每一行都是json的格式 {"prompt": "<prompt text>", "…

【初识爬虫+requests模块】

爬虫又称网络蜘蛛、网络机器人。本质就是程序模拟人使用浏览器访问网站&#xff0c;并将需要的数据抓取下来。爬虫不仅能够使用在搜索引擎领域&#xff0c;在数据分析、商业领域都得到了大规模的应用。 URL 每一个URL指向一个资源&#xff0c;可以是一个html页面&#xff0c;一…

配置git环境与项目创建

项目设计 名称&#xff1a;KOB 项目包含的模块 PK模块&#xff1a;匹配界面&#xff08;微服务&#xff09;、实况直播界面&#xff08;WebSocket协议&#xff09; 对局列表模块&#xff1a;对局列表界面、对局录像界面 排行榜模块&#xff1a;Bot排行榜界面 用户中心模块&…

从Kafka系统中读取消息数据——消费

从Kafka系统中读取消息数据——消费 消费 Kafka 集群中的主题消息检查消费者是不是单线程主题如何自动获取分区和手动分配分区subscribe实现订阅&#xff08;自动获取分区&#xff09;assign&#xff08;手动分配分区&#xff09; 反序列化主题消息反序列化一个类.演示 Kafka 自…

软件测试学习笔记-使用jmeter进行性能测试

性能测试&#xff1a;使用自动化工具&#xff0c;模拟不同的场景&#xff0c;对软件各项性能指标进行测试和评估的过程。 性能测试的目的&#xff1a; 评估当前系统的能力寻找性能瓶颈&#xff0c;优化性能评估软件是否能够满足未来的需要 性能测试和功能测试对比 焦点不同&…

基于FPGA的图像最近邻插值算法verilog实现,包括tb测试文件和MATLAB辅助验证

目录 1.算法运行效果图预览 2.算法运行软件版本 3.部分核心程序 4.算法理论概述 5.算法完整程序工程 1.算法运行效果图预览 将FPGA数据导入matlab显示图片&#xff0c;效果如下&#xff1a; 2.算法运行软件版本 vivado2019.2&#xff0c;matlab2022a 3.部分核心程序 ti…

【开源】SpringBoot框架开发高校学生管理系统

目录 一、摘要1.1 项目介绍1.2 项目录屏 二、功能模块2.1 学生管理模块2.2 学院课程模块2.3 学生选课模块2.4 成绩管理模块 三、系统设计3.1 用例设计3.2 数据库设计3.2.1 学生表3.2.2 学院课程表3.2.3 学生选课表3.2.4 学生成绩表 四、系统展示五、核心代码5.1 查询课程5.2 新…

服务器和CDN推荐

简介 陆云Roovps是一家成立于2021年的主机服务商&#xff0c;主要业务是销售美国服务器、香港服务器及国外湖北十堰高防服务器&#xff0c;还有相关CDN产品。&#xff08; 地址&#xff1a;roovps&#xff09; 一、相关产品

C语言之数据在内存中的存储

目录 1. 整数在内存中的存储2. 大小端字节序和字节序判断什么是大小端&#xff1f;为什么有大小端&#xff1f;练习1练习2练习3练习4练习5练习6 3. 浮点数在内存中的存储浮点数存的过程浮点数取得过程练习题解析 1. 整数在内存中的存储 在讲解操作符的时候&#xff0c;我们已经…

ffmpeg的使用,安装,抽帧,加水印,截图,生成gif,格式转换,抓屏等

实际使用中总结的关于ffmpeg对视频的处理的记录文档 具体信息&#xff1a; http://ffmpeg.org/download.html 官网下载ffmpeg 关于ffmpeg的安装详细步骤和说明 装ffmpeg 方式,Linux和windows下的 http://bbs.csdn.net/topics/390519382 php 调用ffmpeg , http://bbs.csdn.net/t…

(篇九)MySQL常用内置函数

目录 ⌛数学函数 ⌛字符串函数 ⌛聚合函数 ⌛日期函数 &#x1f4d0;获取当前时间 &#x1f4d0;获取时间的某些内容 &#x1f4d0;​编辑 &#x1f4d0;格式化函数 &#x1f4cf;format类型&#xff1a; ⌛系统信息函数 ⌛类型转换函数 数学函数 字符串函数 聚合函…

SSH口令问题

SSH&#xff08;Secure Shell&#xff09;是目前较可靠、专为远程登录会话和其他网络服务提供 安全性的协议&#xff0c;主要用于给远程登录会话数据进行加密&#xff0c;保证数据传输的安全。 SSH口令长度太短或者复杂度不够&#xff0c;如仅包含数字或仅包含字母等时&#xf…