Qt列表下方增加弹出加载数据提示效果

使用QScrollEvent在Qt列表下方弹出提示文字

需求

最近有个需求是在列表下方增加一个“加载更多数据”的提示,当后台在请求数据的时候提示用户。在我的这篇 QScroller的QScrollerProperties参数研究 文章中,研究了一下鼠标手势的列表滚动参数,里面提到了过量滚动的一些设置,可以借用这个效果来实现。

以下是最终效果,为了方便测试,当滚动条处于最底端,并继续尝试滚动时,弹出该提示:

在这里插入图片描述

方案

研究了一下QScroller的源码,发现是通过向目标窗口发送 QScrollEvent 实现滚动和过量滚动的效果,其构造函数第二个参数表示过量滚动的距离。所以直接使用 QVariantAnimation 产生距离动画参数,通过 QApplication::postEvent 直接发送给列表控件。

源码:

  1. 初始化必要的变量和信号绑定
	animation = new QVariantAnimation(this);
    animation->setDuration(100);
    animation->setStartValue(0.0);
    animation->setEndValue(50.0);

    // 向上弹出后保持一定时间
    holdTimer = new QTimer(this);
    holdTimer->setInterval(200);
    holdTimer->setSingleShot(true);

    loadingWidget = new QLabel(this);
    loadingWidget->setText(u8"正在加载更多数据...");
    loadingWidget->setAlignment(Qt::AlignCenter);
    loadingWidget->setVisible(false);

    connect(holdTimer, &QTimer::timeout, animation, [this](){
        // 复用动画,直接切换方向
        animation->setDirection(QAbstractAnimation::Backward);
        animation->start(QAbstractAnimation::KeepWhenStopped);
    });

    connect(animation, &QVariantAnimation::valueChanged, this, [this](QVariant val){
        // 发送QScrollEvent, 指定overshoot参数
        auto scrollbar = this->verticalScrollBar();
        int max = scrollbar->maximum();
        QScrollEvent * se = new QScrollEvent(QPointF(0, max), QPointF(0, val.toReal()), QScrollEvent::ScrollUpdated);
        QApplication::postEvent(this, se);

        int h = this->viewport()->size().height();
        loadingWidget->move(0, h-val.toReal());
    });

    connect(animation, &QVariantAnimation::finished, this, [this](){
        // 发送一个停止状态
        auto scrollbar = this->verticalScrollBar();
        int max = scrollbar->maximum();
        QVariant end = animation->direction() == QAbstractAnimation::Forward ? animation->endValue() : animation->startValue();
        QScrollEvent * se = new QScrollEvent(QPointF(0, max), QPointF(0, end.toReal()), QScrollEvent::ScrollFinished);
        QApplication::postEvent(this, se);

        // 正向动画时,启动延时
        if(animation->direction() == QAbstractAnimation::Forward)
        {
            holdTimer->start();
        }
        else
        {
            loadingWidget->setVisible(false);
        }
    });

  • 向上滚动和向下滚动的动画(animation)只是方向相反,当需要向下滚动时,直接复用动画,设置反向(QAbstractAnimation::Forward)即可
  • 当向上滚动到顶部,增加了一个延迟(holdTimer),保持一段时间后再恢复
  • 使用了一个QLabel做文字提示,这个跟随动画的距离参数,放在列表下方(一定要在动画开始前设置visible
  1. 动画的开启关闭控制
void AutofetchListView::showLoading()
{
    if(holdTimer->isActive())
    {
        // 延时处于激活状态,调用该接口时重新计时
        holdTimer->stop();
        holdTimer->start();
    }
    else
    {
        if(animation->state() == QAbstractAnimation::Running)
        {
            animation->pause();
        }
        else
        {
            QSize s = this->viewport()->size();
            loadingWidget->setGeometry(QRect(0, int(s.height() - 0), s.width(), 50));
            loadingWidget->setVisible(true);
            loadingWidget->raise();

            auto scrollbar = this->verticalScrollBar();
            int max = scrollbar->maximum();
            QScrollEvent * se = new QScrollEvent(QPointF(0, max), QPointF(0, 0), QScrollEvent::ScrollStarted);
            QApplication::postEvent(this, se);
        }
        animation->setDirection(QAbstractAnimation::Forward);
        animation->start(QAbstractAnimation::KeepWhenStopped);
    }
}

void AutofetchListView::stopLoading()
{
    // 停止延时、动画,隐藏提示
    if(holdTimer->isActive())
    {
        holdTimer->stop();
    }
    animation->stop();
    loadingWidget->setVisible(false);
}
  • QScrollEvent 第一个参数是要滚动到的位置,滚动条设置像素滚动时,可以使用滚动条最大值,也就是最底部,第二个参数是在此基础上继续滚动的距离
  • 当处于延时状态,继续尝试滚动时,重新启动延时,这样提示就会一直保持,效果更好
  • 当动画正在运行,继续尝试滚动时,暂停动画并使其重新向上滚动,这样动画看起来更连贯,不会出现跳动(代码中没有区分当前是向上还是向下,如果时正向的,则不需要暂停)
  • 动画没有在运行,说明需要重新提示,开启动画,并将提示文字放在最下方。(这里必须先设置为显示,如果在动画过程中显示,会触发重新布局,Qt内部的一些位置状态不准,出现跳动)

其他问题

  1. 动画的显示时机

    代码中没有贴什么时候应该显示该提示,为了方便测试,我的代码是根据 QWheelEvent 触发的。当滚动条处于最顶部和最底部时,如果继续滚动,QListWidget::wheelEvent 处理后(个人使用 QListWidget测试), QWheelEvent::isAcceptedfalse ,这样可以判断是用户尝试继续滚动。

    Qt的model视图支持动态获取数据,QAbstractItemModel::canFetchMoreQAbstractItemModel::fetchMore 两个接口会在一些情况下自动调用(初始、布局、滚动),当滚动条在最底部且 canFetchMoretrue ,会触发 fetchMore ,可以通过 model 自定义信号通知视图弹出提示,所以根据个人需求实现

  2. 列表背景颜色

    当向上弹出提示时,能看到默认窗口背景跟列表有差异,这个应该可以通过样式表来调整,个人没做测试。

  3. 提示文字方案

    上面代码为了方便,使用了QLabel提示,可以根据自己需求修改。

    如果想在原始窗口上直接绘制提示文字,重写 paintEvent 发现出错,原因是该 paintEventQAbstractScrollArea::viewport 触发的,Qt文档里说明了应该在viewport上绘制,这就出现了冲突。可以通过 installEventFilter(this) ,然后过滤 QEvent::Paint 绘制。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值