QListWidget QListView 和带fetchMore的QListView的性能对比

当界面需要显示大批量数据的时候,在QT框架中,有一个叫做"视图-代理-模型"的设计模式,如果没有自定义绘制的界面需求,也可以叫做"视图-模型".
相比传统的直接在容器控件内添加子控件的方式,这种方法可以大大节省内存的消耗.
本文我们一起测试当数据量达到20万这样级别的时候,不同的设计方法有多大的区别.
首先是QListWidget,我们创建一个QWidget,在中间放上一个QListWidget,就这样,没有任何其他东西,然后在这个QListWidget中添加20万条文本信息,文本内容是"item:123",其中的"123"就是列表项的序号,从一到20万,实在是太简单的例子了,那么他所需要耗费的内存大概是63M
其次登场的是QListView,我们创建一个QWidget,在中间放上一个QListView,就这样,也没有任何其他东西,然后使用一个QStringListModel给这个QListView装载数据,代码类似:

QListView * v = new QListView;
QStringListModel * m = new QStringListModel;
QStringList strList;
qreal len = 0;
for(int i = 0;i < 200000;++i){
	const auto str = "item:" + QString::number(i);
	len += str.capacity() + sizeof(QString);
	strList.push_back(str);
}
m->setStringList(strList);
v->setModel(m);

这样的方式耗费的内存大概是24M , 内存节省了62%
在这里插入图片描述
上图,第三个程序使用QListWidget , 第二个程序使用QListView
但是这么简单的程序如果耗费24M内存,仍然是不合理的.
在很多业务逻辑中,大批量数据的显示往往是通过分页的方式来节约内存的,用户选择页码,点击跳转后,界面加载对应的那一部分内容.
除了这个聪明的做法还有一种方式就是延迟加载,通过实现QAbstractListView的虚函数void fetchMore(const QModelIndex &parent) override来实现.
这个虚函数的常规实现类似这样的:

void fetchMore(const QModelIndex &parent) override {
	const int total = mDataList.size();
	if(total > TOTAL) {
		return;
	}
	const int batch = 100;
	emit layoutAboutToBeChanged();
	beginInsertRows(QModelIndex(), total, total + batch -1);
	for(int i = 0;i < batch;++i){
		mDataList.push_back("item:" + QString::number(total + i));
	}
	endInsertRows();
	emit layoutChanged();
}

当用户拖动滚动条到底部的时候,QListView会问模型索要更多数据,这时候这个虚函数就会被执行了,在这里面,判断当前已经显示的数量和总数量的大小关系,来给当前数据链表添加更多的数据,在实际业务中,上述代码的第十行可能是个查询数据库的操作,也可能是查询网络来加载更多数据的操作.
当使用这种方式的时候,我们发现,内存使用缩小到了5.7M ,但是这样的方式会随着用户的不断阅览而不断增加内存的消耗.在QWidget框架中还没有一个接口类似fetchLess那样可以告诉模型回收一些数据.
在QML中通过设置cacheBuffer,可以把大列表的内存消耗降低到用户想要的程度,并且保持这样.

Window {
    visible: true
    width: 640
    height: 480
    Component {
        id: idTmpItemDelegate
        Rectangle{
            width: 200; height: 32
            Column {
                Text { text: "item:" + index }
            }
        }
    }
    ListView {
        height: 480
        width: 200;
        cacheBuffer: 100;
        id:idListView
        model: 200000
        delegate: idTmpItemDelegate
    }
}

这看上去非常神奇,其实是它非常阴险地隐藏了滚动条,而且在快速滚动过程中总觉得有时候比较卡顿.
于是我们可以在QListView中实现类似的稳定低内存展示大批量数据的模式.首先隐藏滚动条,其次在滚动鼠标事件中捕获滚动的方向,根据滚动的偏移量滑动滚动条,当滚动条当前位置接近1的时候,照旧调用fetchMore,并且判断当前总数据是否大于一定的限值,是的话就删除一部分数据,这就保证了内存消耗不再永久增加了.而用户看不到滚动条的长短变化,根本毫无感知.
但是这样的实现并不高级 , 各位不需要对充满复杂的逻辑考虑的实现而灰心丧气. 实际大批量的业务数据的展示要么是用户根本不关心的,要么是用户非常关心的. 如果是不关心的 , 那么只显示一部分小数据已经仁至义尽了 , 如果是非常关心的 , 那么分页或者使用过滤排序模型显然更为合理. 这两种模式都涉及动态的加载, 这一操作带来的延时是说得过去的 ,因为用户知道他在查询很大的数据.

  • 18
    点赞
  • 21
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值