Qt实践:一个简单的丝滑侧滑栏实现

Qt实践:一个简单的丝滑侧滑栏实现

笔者前段时间突然看到了侧滑栏,觉得这个抽屉式的侧滑栏非常的有趣,打算这里首先尝试实现一个简单的丝滑侧滑栏。

首先是上效果图

(C,GIF帧率砍到毛都不剩了)

QPropertyAnimation

官方的QPropertyAnimation Class | Qt Core 6.8.1

也就是说,这个类封装了我们的Qt动画播放的类,我们针对Widgets的属性对其变化进行动画播放。Qt的抽象非常的好,只需要设置我们的起点和终点状态,以及设置一下时间间隔和播放的变化方式,就完事了。

  • setDuration(int msec): 设置动画的持续时间,单位是毫秒。

  • setStartValue(const QVariant &startValue): 设置动画的起始值。

  • setEndValue(const QVariant &endValue): 设置动画的结束值。

  • setEasingCurve(const QEasingCurve &curve): 设置动画的插值曲线,控制动画的速度变化(如加速、减速、匀速等)。常用的曲线类型有 QEasingCurve::LinearQEasingCurve::InQuadQEasingCurve::OutBounce 等。

笔者实现的效果的API就是用到了上面四个。

首先我们思考一下,SideBar看似是一个侧滑栏,但是跟随变动的,考虑上夹在中间的按钮,是三个部分。我们按照上面的思考思路。

1. 隐藏侧边栏(do_hide_animations

使侧边栏从可见状态过渡到隐藏状态。具体变化如下:

  • 侧边栏动画 (animation_side)

    • 起始状态:侧边栏的当前几何位置(ui->widgetSiderBar->geometry())。

    • 结束状态:侧边栏移动到视图外部,即其横坐标变为负值,具体位置为 ( - ui->widgetSiderBar->width(), ui->widgetSiderBar->y() )。这样侧边栏就被“隐藏”到屏幕外。

  • 按钮动画 (animation_button)

    • 起始状态:操作按钮当前的几何位置(ui->btn_operate->geometry())。

    • 结束状态:按钮的位置将移动到屏幕左侧,具体位置为 ( 0, ui->btn_operate->y() )。这样按钮会被移到左侧,表示侧边栏已隐藏。

  • 主界面动画 (animation_main)

    • 起始状态:主界面的当前几何位置(ui->widget_mainside->geometry())。

    • 结束状态:主界面位置根据按钮的位置进行调整,具体为 ( ui->btn_operate->width(), ui->widget_mainside->y() ),这意味着主界面会向左移动,避开被隐藏的侧边栏。

  • 操作按钮文本

    • 操作按钮的文本更改为 ">",表示点击后侧边栏会“展开”。

  • 执行动画:调用 group->start() 启动所有动画,产生隐藏效果。

2. 显示侧边栏(do_show_animations

当用户点击按钮以显示侧边栏时,执行 do_show_animations,将侧边栏从隐藏状态恢复到可见状态。具体变化如下:

  • 侧边栏动画 (animation_side)

    • 起始状态:侧边栏当前的几何位置(ui->widgetSiderBar->geometry())。

    • 结束状态:侧边栏移动到其原始位置,即横坐标变为 0,具体位置为 ( 0, ui->widgetSiderBar->y() ),使其重新显示在屏幕上。

  • 按钮动画 (animation_button)

    • 起始状态:操作按钮当前的几何位置(ui->btn_operate->geometry())。

    • 结束状态:按钮的位置将移动到侧边栏的右侧,具体为 ( ui->widgetSiderBar->width(), ui->btn_operate->y() ),表示按钮回到右侧,侧边栏已重新显示。

  • 主界面动画 (animation_main)

    • 起始状态:主界面的当前几何位置(ui->widget_mainside->geometry())。

    • 结束状态:主界面的位置调整为 ( ui->widgetSiderBar->width() + ui->btn_operate->width(), ui->widget_mainside->y() ),并且宽度变为 width() - ui->btn_operate->width() - ui->widgetSiderBar->width(),使得主界面重新适应显示的侧边栏。

  • 操作按钮文本

    • 操作按钮的文本更改为 "<",表示点击后侧边栏会“隐藏”。

  • 执行动画:调用 group->start() 启动所有动画,产生显示效果。

代码上的体现就是

void SideBarWidget::do_hide_animations() {
    animation_side->setStartValue(ui->widgetSiderBar->geometry());
    /* move to the hidden place */
    animation_side->setEndValue(
        QRect(-ui->widgetSiderBar->width(), ui->widgetSiderBar->y(),
              ui->widgetSiderBar->width(), ui->widgetSiderBar->height()));
​
    animation_button->setStartValue(ui->btn_operate->geometry());
    animation_button->setEndValue(QRect(0, ui->btn_operate->y(),
                                        ui->btn_operate->width(),
                                        ui->btn_operate->height()));
​
    animation_main->setStartValue(ui->widget_mainside->geometry());
    animation_main->setEndValue(QRect(
        ui->btn_operate->width(), ui->widget_mainside->y(),
        width() - ui->btn_operate->width(), ui->widget_mainside->height()));
​
    ui->btn_operate->setText(">");
    group->start();
}
void SideBarWidget::do_show_animations() {
    animation_side->setStartValue(ui->widgetSiderBar->geometry());
    /* move to the hidden place */
    animation_side->setEndValue(QRect(0, ui->widgetSiderBar->y(),
                                      ui->widgetSiderBar->width(),
                                      ui->widgetSiderBar->height()));
​
    animation_button->setStartValue(ui->btn_operate->geometry());
    animation_button->setEndValue(
        QRect(ui->widgetSiderBar->width(), ui->btn_operate->y(),
              ui->btn_operate->width(), ui->btn_operate->height()));
​
    animation_main->setStartValue(ui->widget_mainside->geometry());
    animation_main->setEndValue(
        QRect(ui->widgetSiderBar->width() + ui->btn_operate->width(),
              ui->widget_mainside->y(),
              width() - ui->btn_operate->width() - ui->widgetSiderBar->width(),
              ui->widget_mainside->height()));
    ui->btn_operate->setText("<");
    ui->widgetSiderBar->setVisible(true);
    group->start();
}
上面体现了一个优化,那就是使用动画组Group来同步的进行操作。防止出现动画抢跑。

源码

完整的测试源码在:CCQt_Libs/Widget/SideBarWidget at main · Charliechen114514/CCQt_Libs (github.com)

C++源码如下

#include "SideBarWidget.h"
#include "ui_SideBarWidget.h"
#include <QParallelAnimationGroup>
#include <QPropertyAnimation>

namespace SideBarUtilsTools {
void clearLayout(QLayout* layout) {
    if (!layout) return;

    QLayoutItem* item;
    while ((item = layout->takeAt(0)) != nullptr) {
        if (item->widget()) {
            item->widget()->hide();  // 隐藏控件,但不删除
        } else {
            clearLayout(item->layout());  // 递归清理子布局
        }
    }
}
}  // namespace SideBarUtilsTools

SideBarWidget::SideBarWidget(QWidget* parent)
    : QWidget(parent), ui(new Ui::SideBarWidget) {
    ui->setupUi(this);
    __initMemory();
    __initConnection();
}

void SideBarWidget::switch_state() {
    setState(!hidden_state);
}

void SideBarWidget::switch_button_visible() {
    setButtonVisible(!ui->btn_operate->isVisible());
}

void SideBarWidget::removeLayout(Role r) {
    switch (r) {
        case Role::SideBar:
            SideBarUtilsTools::clearLayout(ui->widgetSiderBar->layout());
            break;
        case Role::MainSide:
            SideBarUtilsTools::clearLayout(ui->widget_mainside->layout());
            break;
    }
}

void SideBarWidget::setButtonVisible(bool visible) {
    ui->btn_operate->setVisible(visible);
    ui->btn_operate->setText(hidden_state ? ">" : "<");
}

void SideBarWidget::addLayout(QLayout* layout, const QWidgetList& widgetList,
                              Role r) {
    switch (r) {
        case Role::SideBar:
            ui->widgetSiderBar->setLayout(layout);
            for (auto& w : widgetList) {
                ui->widgetSiderBar->layout()->addWidget(w);
            }
            break;
        case Role::MainSide:
            ui->widget_mainside->setLayout(layout);
            for (auto& w : widgetList) {
                ui->widget_mainside->layout()->addWidget(w);
            }
            break;
    }
}

/* setTypes */
void SideBarWidget::setAnimationDuration(int duration) {
    animation_button->setDuration(duration);
    animation_main->setDuration(duration);
    animation_side->setDuration(duration);
}
void SideBarWidget::setAnimationCurve(QEasingCurve::Type curve) {
    animation_button->setEasingCurve(curve);
    animation_main->setEasingCurve(curve);
    animation_side->setEasingCurve(curve);
}

void SideBarWidget::__initMemory() {
    animation_main = new QPropertyAnimation(ui->widget_mainside, "geometry");
    animation_main->setDuration(SideBarWidgetStaticConfig::ANIMATION_DURATION);
    animation_main->setEasingCurve(SideBarWidgetStaticConfig::ANIMATION_CURVE);
    animation_side = new QPropertyAnimation(ui->widgetSiderBar, "geometry");
    animation_side->setDuration(SideBarWidgetStaticConfig::ANIMATION_DURATION);
    animation_side->setEasingCurve(SideBarWidgetStaticConfig::ANIMATION_CURVE);
    animation_button = new QPropertyAnimation(ui->btn_operate, "geometry");
    animation_button->setDuration(
        SideBarWidgetStaticConfig::ANIMATION_DURATION);
    animation_main->setDuration(SideBarWidgetStaticConfig::ANIMATION_DURATION);
    group = new QParallelAnimationGroup(this);
    group->addAnimation(animation_main);
    group->addAnimation(animation_side);
    group->addAnimation(animation_button);
}

void SideBarWidget::__initConnection() {
    connect(ui->btn_operate, &QPushButton::clicked, this,
            [this]() { setState(!hidden_state); });
    connect(group, &QParallelAnimationGroup::finished, this, [this] {
        ui->widgetSiderBar->setVisible(!hidden_state);
        // have no better idea :(, to update the layout
        resize(size().width() + 1, size().height() + 1);
        resize(size().width() - 1, size().height() - 1);
    });
}

void SideBarWidget::do_hide_animations() {
    animation_side->setStartValue(ui->widgetSiderBar->geometry());
    /* move to the hidden place */
    animation_side->setEndValue(
        QRect(-ui->widgetSiderBar->width(), ui->widgetSiderBar->y(),
              ui->widgetSiderBar->width(), ui->widgetSiderBar->height()));

    animation_button->setStartValue(ui->btn_operate->geometry());
    animation_button->setEndValue(QRect(0, ui->btn_operate->y(),
                                        ui->btn_operate->width(),
                                        ui->btn_operate->height()));

    animation_main->setStartValue(ui->widget_mainside->geometry());
    animation_main->setEndValue(QRect(
        ui->btn_operate->width(), ui->widget_mainside->y(),
        width() - ui->btn_operate->width(), ui->widget_mainside->height()));

    ui->btn_operate->setText(">");
    group->start();
}
void SideBarWidget::do_show_animations() {
    animation_side->setStartValue(ui->widgetSiderBar->geometry());
    /* move to the hidden place */
    animation_side->setEndValue(QRect(0, ui->widgetSiderBar->y(),
                                      ui->widgetSiderBar->width(),
                                      ui->widgetSiderBar->height()));

    animation_button->setStartValue(ui->btn_operate->geometry());
    animation_button->setEndValue(
        QRect(ui->widgetSiderBar->width(), ui->btn_operate->y(),
              ui->btn_operate->width(), ui->btn_operate->height()));

    animation_main->setStartValue(ui->widget_mainside->geometry());
    animation_main->setEndValue(
        QRect(ui->widgetSiderBar->width() + ui->btn_operate->width(),
              ui->widget_mainside->y(),
              width() - ui->btn_operate->width() - ui->widgetSiderBar->width(),
              ui->widget_mainside->height()));
    ui->btn_operate->setText("<");
    ui->widgetSiderBar->setVisible(true);
    group->start();
}

SideBarWidget::~SideBarWidget() {
    delete ui;
}

接口文件如下:

#ifndef SIDEBARWIDGET_H
#define SIDEBARWIDGET_H

#include <QEasingCurve>
#include <QWidget>
class QPropertyAnimation;
class QParallelAnimationGroup;
namespace SideBarWidgetStaticConfig {
static constexpr const bool               INIT_STATE         = false;
static constexpr const int                ANIMATION_DURATION = 500;
static constexpr const QEasingCurve::Type ANIMATION_CURVE =
    QEasingCurve::InOutQuad;
};  // namespace SideBarWidgetStaticConfig

namespace Ui {
class SideBarWidget;
}

class SideBarWidget : public QWidget {
    Q_OBJECT

public:
    explicit SideBarWidget(QWidget* parent = nullptr);
    void inline showSideBar() {
        setState(false);
    }
    void inline hideSideBar() {
        setState(true);
    }
    enum class Role { SideBar, MainSide };
    /* addWidgets to the two sides */
    void addLayout(QLayout* layout, const QWidgetList& widgetList, Role r);
    /* remove the display widgets */
    void removeLayout(Role r);
    /* enable or disable the button visibilities */
    void setButtonVisible(bool visible);
    /* setTypes and durations */
    void setAnimationDuration(int duration);
    void setAnimationCurve(QEasingCurve::Type curve);

    ~SideBarWidget();
public slots:
    void switch_state();
    void switch_button_visible();

private:
    QPropertyAnimation*      animation_main;
    QPropertyAnimation*      animation_side;
    QPropertyAnimation*      animation_button;
    QParallelAnimationGroup* group;
    void inline setState(bool st) {
        hidden_state = st;
        hidden_state ? do_hide_animations() : do_show_animations();
    }
    void               __initMemory();
    void               __initConnection();
    void               do_hide_animations();
    void               do_show_animations();
    bool               hidden_state{SideBarWidgetStaticConfig::INIT_STATE};
    Ui::SideBarWidget* ui;
};

#endif  // SIDEBARWIDGET_H

Reference

感谢https://zhuanlan.zhihu.com/p/614475116?utm_id=0,我的设计几乎从这里派生出来!

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

如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈qq邮箱809451989@qq.com,一经查实,立即删除!

相关文章

【Linux网络编程】传输层协议

目录 一&#xff0c;传输层的介绍 二&#xff0c;UDP协议 2-1&#xff0c;UDP的特点 2-2&#xff0c;UDP协议端格式 三&#xff0c;TCP协议 3-1&#xff0c;TCP报文格式 3-2&#xff0c;TCP三次握手 3-3&#xff0c;TCP四次挥手 3-4&#xff0c;滑动窗口 3-5&#xf…

[C]基础8.详解操作符

博客主页&#xff1a;算法歌者本篇专栏&#xff1a;[C]您的支持&#xff0c;是我的创作动力。 文章目录 0、总结1、操作符的分类2、二进制和进制转换2.1、2进制转10进制2.2、10进制转2进制2.3、2进制转8进制和16进制 3、原码、反码、补码4、移位操作符4.1 左移操作符4.2 右移操…

基于Springboot用axiospost请求接收字符串参数为null的解决方案

问题 ​ 今天在用前端 post 请求后端时发现&#xff0c;由于是以 Json对象的形式传输的&#xff0c;后端用两个字符串形参无法获取到对应的参数值 前端代码如下&#xff1a; axios.post(http://localhost:8083/test/postParams,{a: 1, b:2} ,{Content-Type: application/jso…

数据结构——堆(介绍,堆的基本操作、堆排序)

我是一个计算机专业研0的学生卡蒙Camel&#x1f42b;&#x1f42b;&#x1f42b;&#xff08;刚保研&#xff09; 记录每天学习过程&#xff08;主要学习Java、python、人工智能&#xff09;&#xff0c;总结知识点&#xff08;内容来自&#xff1a;自我总结网上借鉴&#xff0…

【Qt】05-菜单栏

做菜单 前言一、创建文件二、菜单栏 QMenuBar2.1 示例代码2.2 运行结果 三、工具栏 QToolBar3.1 运行代码3.2 结果分析 四、状态栏 QStatusBar4.1 运行代码4.2 运行结果 五、文本编辑框 QTextEdit5.1 运行代码5.2 运行结果 六、浮动窗口 addDockWidget6.1 运行代码6.2 运行结果…

【喜讯】海云安荣获“数字安全产业贡献奖”

近日&#xff0c;国内领先的数字化领域独立第三方调研咨询机构数世咨询主办的“2025数字安全市场年度大会”在北京成功举办。在此次大会上&#xff0c;海云安的高敏捷信创白盒产品凭借其在AI大模型技术方面的卓越贡献和突出的技术创新能力&#xff0c;荣获了“数字安全产业贡献…

MySQL训练营-慢查询诊断问题

slow_query_log long_query_time slow_query_log&#xff1a;日志开关&#xff0c;是否记慢查询日志 long_query_time&#xff1a;超过多长时间判定为慢查询 查看参数设置&#xff1a; SHOW VARIABLES LIKE ‘slow_query_log’;SHOW VARIABLES LIKE ‘long_query_time’; …

2025年最新汽车零部件企业销售项目管理解决方案

在汽车零部件企业&#xff0c;销售项目管理的不规范和销售预测的不准确性常导致生产计划无法及时调整&#xff0c;因此客户关系常常中断&#xff0c;导致企业业务机会的丧失。为解决该问题&#xff0c;企业需要投入更多资源以优化销售流程与销售预测。 1、360多维立体客户视图…

K8S中ingress详解

Ingress介绍 Kubernetes 集群中&#xff0c;服务&#xff08;Service&#xff09;是一种抽象&#xff0c;它定义了一种访问 Pod 的方式&#xff0c;无论这些 Pod 如何变化&#xff0c;服务都保持不变。服务可以被映射到一个静态的 IP 地址&#xff08;ClusterIP&#xff09;、一…

大模型:LangChain技术讲解

一、什么是LangChain 1、介绍 LangChain是一个用于开发由大型语言模型提供支持的Python框架。它提供了一系列工具和组件&#xff0c;帮助我们将语言模型集成到自己的应用程序中。 有了它之后&#xff0c;我们可以更轻松地实现对话系统、文本生成、文本分类、问答系统等功能。…

【优选算法篇】2----复写零

---------------------------------------begin--------------------------------------- 这道算法题相对于移动零&#xff0c;就上了一点点强度咯&#xff0c;不过还是很容易理解的啦~ 题目解析&#xff1a; 这道题如果没理解好题目&#xff0c;是很难的&#xff0c;但理解题…

office 2019 关闭word窗口后卡死未响应

最近关闭word文件总是出现卡死未响应的状态&#xff0c;必须从任务管理器才能杀掉word 进程&#xff0c;然后重新打开word再保存&#xff0c;很是麻烦。&#xff08;#其他特征&#xff0c;在word中打字会特别变慢&#xff0c;敲击键盘半秒才出现字符。&#xff09; office官网…

acm培训 part 1(学习总结)

第一部分的重点为语法糖&#xff0c;时空复杂度&#xff0c;stl容器等等&#xff0c;下面就简单介绍一下这些部分。 1. 语法糖 1.1 定义 语法糖是由英国计算机科学家彼得约翰兰达提出的一个术语&#xff0c;指的是编程语言中添加的某种语法&#xff0c;这种语法对语言的功能…

Arduino基础入门学习——OLED显示屏+DHT11采集温湿度并显示

Arduino基础入门学习——OLED显示屏DHT11显示温湿度 一、前言二、准备工作三、程序代码四、结束语 一、前言 本篇文章主要使用OLED液晶显示屏模块和DHT11温湿度传感器&#xff0c;获取环境温湿度并显示在显示屏&#xff0c;也算是结合之前我所编写的博客给大家带来一个算是比较…

Kubernetes相关知识入门详解

一、Pod的滚动升级 1.服务升级的一般思路&#xff1a;停止与该服务相关的所有服务pod&#xff0c;重新拉去更新后的镜像并启动。这种方法存在一个比较现实的问题是逐步升级导致较长时间的服务不可用。 2.Kubernetes滚动升级的思路&#xff1a;通过滚动升级的命令创建新的rc&…

云原生时代,如何构建高效分布式监控系统

文章目录 一.监控现状二.Thanos原理分析SidecarQuerierStoreCompactor 三.Sidecar or ReceiverThanos Receiver工作原理 四.分布式运维架构 一.监控现状 Prometheus是CNCF基金会管理的一个开源监控项目&#xff0c;由于其良好的架构设计和完善的生态&#xff0c;迅速成为了监控…

Qt 5.14.2 学习记录 —— 십구 事件

文章目录 1、事件的概念2、处理事件3、鼠标事件1、鼠标单击和双击2、鼠标移动3、鼠标滚轮滚动 4、键盘事件5、定时器事件6、窗口移动和大小改变事件 1、事件的概念 用户进行操作时会产生事件&#xff0c;事件可以关联处理函数。Qt封装了操作系统的事件机制&#xff0c;然后进一…

10. SpringCloud Alibaba Sentinel 规则持久化部署详细剖析

10. SpringCloud Alibaba Sentinel 规则持久化部署详细剖析 文章目录 10. SpringCloud Alibaba Sentinel 规则持久化部署详细剖析1. 规则持久化1.1 Nacos Server 配置中心-规则持久化实例 2. 最后&#xff1a; 1. 规则持久化 规则没有持久化的问题 如果 sentinel 流控规则没有…

地学专业想提前准备春招?怎么准备自己的简历?

眼看着即将过年&#xff0c;过完年后基本上春招也要开始提上日程 之前咱们说过&#xff0c;很多同学认为自身技术过硬就会一路顺风&#xff0c;自己经验丰富、编程技术过硬&#xff0c;就不愁找不到工作&#xff0c;这固然是取得好offer的基础。 但再好的技术也不可能通过混乱…

IoTDB结合Mybatis使用示例(增删查改自定义sql等)

IoTDB时序库是当前越来越流行以及基于其优势各大厂商越来越易接受的国产开源时序数据库&#xff0c;针对IoTDB的内容不做过多介绍&#xff0c;在使用该时序库时&#xff0c;往往有一定入门门槛&#xff0c;不同于关系型数据库或文档型数据库那般方便维护和接入开发&#xff0c;…