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 绘制。

  • 5
    点赞
  • 6
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
### 回答1: 在Qt中,我们可以通过使用QMenu和QPushButton类来实现下方弹出子菜单。 首先,我们需要创建一个QMenu对象,表示我们要弹出的子菜单。然后,我们可以使用QMenu的addAction()方法来向子菜单中添加动作。每个动作都会显示为菜单中的一个选项。 接下来,我们可以创建一个QPushButton对象,表示需要添加子菜单的按钮。使用QPushButton的setMenu()方法,将上一步创建的QMenu对象设置为按钮的下拉菜单。 最后,我们需要在需要弹出子菜单的时候,通过调用QPushButton的showMenu()方法来显示子菜单。 下面是一个简单的示例代码,实现了一个带有下方弹出子菜单的QPushButton: ```cpp #include <QtWidgets> int main(int argc, char *argv[]) { QApplication app(argc, argv); // 创建一个子菜单 QMenu *subMenu = new QMenu("子菜单"); subMenu->addAction("动作1"); subMenu->addAction("动作2"); // 创建一个按钮 QPushButton *button = new QPushButton("按钮"); button->setMenu(subMenu); // 显示窗口 QWidget window; QVBoxLayout *layout = new QVBoxLayout; layout->addWidget(button); window.setLayout(layout); window.show(); return app.exec(); } ``` 在这个示例中,我们创建了一个名为"子菜单"的QMenu对象,并向其添加了两个动作。然后,我们创建了一个QPushButton对象,将子菜单设置为按钮的下拉菜单。最后,我们通过调用按钮的showMenu()方法,在点击按钮时显示子菜单。 以上就是使用Qt实现下方弹出子菜单的简单方法。希望可以帮助到你! ### 回答2: 在Qt中,可以通过QMenu和QPushButton来实现按钮下方弹出子菜单的效果。具体步骤如下: 1. 创建一个QPushButton对象,作为主按钮。例如: QPushButton *mainButton = new QPushButton("主按钮"); 2. 创建一个QMenu对象,用于作为主按钮的子菜单。例如: QMenu *subMenu = new QMenu(); 3. 在子菜单中添加需要的子菜单项,使用addAction方法。例如: subMenu->addAction("子菜单项1"); subMenu->addAction("子菜单项2"); 4. 将子菜单设置给主按钮,使用setMenu方法。例如: mainButton->setMenu(subMenu); 5. 还可以设置主按钮的弹出方式,使用setPopupMode方法。例如: mainButton->setPopupMode(QPushButton::InstantPopup); // 点击即弹出 6. 最后,将主按钮添加到布局中显示即可。 在运行程序时,当点击主按钮时,子菜单会在主按钮下方弹出显示,用户可以选择相应的子菜单项。 需要注意的是,该方法适用于Qt中的QWidget类及其子类的程序界面中,如果是其他程序界面库,具体方法和步骤可能会有所不同。 ### 回答3: 在Qt中,可以使用QMenu实现QPushButton下方弹出子菜单。首先,创建一个QPushButton和一个QMenu对象。然后,将QMenu对象设置为QPushButton的菜单。接下来,可以使用QMenu的addAction方法添加子菜单项。最后,通过调用QPushButton的showMenu方法,可以在按钮下方弹出子菜单。 下面是一个简单的示例代码: ```cpp #include <QtWidgets> int main(int argc, char *argv[]) { QApplication app(argc, argv); QMainWindow window; QWidget *centralWidget = new QWidget(&window); QVBoxLayout *layout = new QVBoxLayout; QPushButton *button = new QPushButton("按钮", centralWidget); QMenu *subMenu = new QMenu(button); subMenu->addAction("子菜单项1"); subMenu->addAction("子菜单项2"); button->setMenu(subMenu); layout->addWidget(button); centralWidget->setLayout(layout); window.setCentralWidget(centralWidget); window.show(); return app.exec(); } ``` 以上代码创建了一个包含一个QPushButton的主窗口。QPushButton设置了一个QMenu作为其菜单,其中包含两个子菜单项。运行程序后,点击按钮时,按钮下方弹出一个子菜单,其中包含两个选项。 希望以上内容能帮到你!

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值