广东省城乡建设厅投诉网站首页,公司产品展示网站源码,做早餐烧菜有什么网站,触屏网站QML自带的GridView只能定义delegate#xff0c;没有section#xff0c;类似手机相册带时间分组标签的样式就没法做。最简单的方式就是组合ListViewGridView#xff0c;或者ListViewFlow#xff0c;但是嵌套View时#xff0c;子级View一般是完全展开的#xff0c;只显示该…QML自带的GridView只能定义delegate没有section类似手机相册带时间分组标签的样式就没法做。最简单的方式就是组合ListViewGridView或者ListViewFlow但是嵌套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: blackText {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;QHashint, QByteArray roleNames() const override;// 在头部添加一个数据Q_INVOKABLE void appendData(int value, const QString date);// 根据 model.index 删除一个数据Q_INVOKABLE void removeData(int index);// 加点测试数据void test();private:QVectorDataInfo datas;QVectorSectionInfo 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();
}QHashint, QByteArray DataModel::roleNames() const
{static QHashint, 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: groupNamecriteria: ViewSection.FullStringdelegate: Item {width: list_view.width - control.paddingheight: 40Rectangle {width: parent.widthheight: parent.height - control.spacingcolor: grayText {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: blackText {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: appendonClicked: {list_model.appendData(model.value, 2222.2.22)}}Button {width: 100height: 30text: removeonClicked: {list_model.removeData(model.index)}}}}} // end delegate Item} // end ListView}
}这里只是实现了一个简单的效果很多细节还需要调整。
通过添加更多的属性和计算也可以实现带section的FlowView即Item的宽高不是固定大小整体为流式布局。