QT自定义开关按钮,实现手机风格的开关效果
先看效果图

项目结构文件如下,没有使用ui文件

untitled8.pro项目文件内容
QT += core gui greaterThan(QT_MAJOR_VERSION, 4): QT += widgets CONFIG += c++17 # You can make your code fail to compile if it uses deprecated APIs. # In order to do so, uncomment the following line. #DEFINES += QT_DISABLE_DEPRECATED_BEFORE=0x060000 # disables all the APIs deprecated before Qt 6.0.0 SOURCES += \ main.cpp \ mainwindow.cpp HEADERS += \ ToggleSwitch.h \ mainwindow.h FORMS += # Default rules for deployment. qnx: target.path = /tmp/$${TARGET}/bin else: unix:!android: target.path = /opt/$${TARGET}/bin !isEmpty(target.path): INSTALLS += target
头文件内容
// mainwindow.h #ifndef MAINWINDOW_H #define MAINWINDOW_H #include <QMainWindow> #include "ToggleSwitch.h" class MainWindow : public QMainWindow { Q_OBJECT public: explicit MainWindow(QWidget *parent = nullptr); ~MainWindow(); private: ToggleSwitch *togglePort; void initUI(); void initConnections(); }; #endif // MAINWINDOW_H
/* ToggleSwitch.h */ #ifndef TOGGLESWITCH_H #define TOGGLESWITCH_H #include <QAbstractButton> #include <QPainter> #include <QPropertyAnimation> #include <QMouseEvent> /** * @brief 自定义开关按钮(Toggle Switch),继承自 QAbstractButton * * 通过绘制圆角矩形轨道和圆形滑块,实现 iOS 风格的开关效果。 * 支持点击切换状态,并带有滑动动画。 */ class ToggleSwitch : public QAbstractButton { Q_OBJECT // 声明一个可动画的属性:offset,用于控制滑块水平偏移量 // WRITE 表示可写,NOTIFY 表示属性变化时发出 offsetChanged 信号 Q_PROPERTY(int offset READ offset WRITE setOffset NOTIFY offsetChanged) public: /** * @brief 构造函数 * @param parent 父组件指针,默认为 nullptr */ explicit ToggleSwitch(QWidget *parent = nullptr) : QAbstractButton(parent) , m_offset(0) // 初始滑块偏移量为 0 { // 设置按钮为可切换状态,以维护 on/off 两种状态 setCheckable(true); // 固定大小策略,不随布局伸缩 setSizePolicy(QSizePolicy::Fixed, QSizePolicy::Fixed); // 鼠标悬停时显示手型光标 setCursor(Qt::PointingHandCursor); // 创建属性动画,用于滑块平滑移动 m_animation = new QPropertyAnimation(this, "offset", this); m_animation->setDuration(120); // 动画时长 120 毫秒 } /** * @brief 推荐的组件大小 * @return QSize 推荐宽度 50,高度 30 */ QSize sizeHint() const override { return QSize(50, 30); } /** @brief 读取当前滑块偏移量 */ int offset() const { return m_offset; } /** * @brief 设置滑块偏移量(动画执行时调用) * @param off 新的偏移量值 */ void setOffset(int off) { if (m_offset == off) return; // 若无变化,则直接返回 m_offset = off; // 更新偏移量 update(); // 触发重绘 emit offsetChanged(off); // 发出属性变化信号 } signals: /** * @brief offset 属性变化通知信号 * @param offset 新的偏移量 */ void offsetChanged(int offset); protected: /** * @brief 重写绘制事件 * @param 未使用的 QPaintEvent 指针 */ void paintEvent(QPaintEvent *) override { QPainter p(this); p.setRenderHint(QPainter::Antialiasing); // 开启抗锯齿 // ---- 绘制轨道 ---- // 轨道区域:从 (0,0) 开始,宽度为控件宽度,高度为控件高度 QRectF trackRect(0, 0, width(), height()); // 根据开关状态选择轨道颜色:打开时绿色,关闭时浅灰色 QColor trackColor = isChecked() ? QColor(0, 200, 0) // 打开:绿色 : QColor(200, 200, 200); // 关闭:浅灰色 p.setBrush(trackColor); p.setPen(Qt::NoPen); // 不绘制边框 // 圆角半径设为高度一半,使轨道呈胶囊形 p.drawRoundedRect(trackRect, height() / 2, height() / 2); // ---- 绘制滑块(thumb) ---- // 滑块直径比控件高度小 4 像素 int diameter = height() - 4; // 滑块位置:水平偏移 m_offset + 左边距 2 像素,垂直偏移 2 像素 QRectF thumbRect(2 + m_offset, 2, diameter, diameter); p.setBrush(Qt::white); // 滑块填充白色 p.drawEllipse(thumbRect); // 绘制圆形滑块 } /** * @brief 重写鼠标释放事件,响应点击切换 * @param e 鼠标事件指针 */ void mouseReleaseEvent(QMouseEvent *e) override { // 仅在左键释放且点击区域在控件内部时才处理切换逻辑 if (e->button() == Qt::LeftButton && rect().contains(e->pos())) { // 调用父类,切换 checked 状态并发射 toggled() 信号 QAbstractButton::mouseReleaseEvent(e); // 重新配置动画起始值和结束值 m_animation->stop(); // 停止当前动画 m_animation->setStartValue(m_offset); // 当前偏移作为起点 // 目标偏移:打开时移动到 (宽度 - 高度),否则回到 0 int endValue = isChecked() ? (width() - height()) : 0; m_animation->setEndValue(endValue); // 设置终点 m_animation->start(); // 启动动画 } else { // 其他情况交由父类处理(如键盘激活等) QAbstractButton::mouseReleaseEvent(e); } } private: int m_offset; // 滑块当前水平偏移量 QPropertyAnimation *m_animation; // 控制滑块移动的动画对象 }; #endif // TOGGLESWITCH_H
源文件内容
// main.cpp #include <QApplication> #include "mainwindow.h" int main(int argc, char *argv[]) { QApplication a(argc, argv); MainWindow w; w.show(); return a.exec(); }
// mainwindow.cpp #include "mainwindow.h" #include <QHBoxLayout> #include <QWidget> #include <QMessageBox> MainWindow::MainWindow(QWidget *parent) : QMainWindow(parent) { initUI(); initConnections(); } MainWindow::~MainWindow() { } void MainWindow::initUI() { // 创建一个中央 widget 和布局 QWidget *central = new QWidget(this); auto *layout = new QHBoxLayout(central); // 新建 ToggleSwitch togglePort = new ToggleSwitch; // togglePort->setToolTip("打开/关闭串口"); layout->addStretch(); layout->addWidget(togglePort); layout->addStretch(); setCentralWidget(central); resize(200, 100); } void MainWindow::initConnections() { // 监听滑动开关状态 connect(togglePort, &ToggleSwitch::toggled, this, [this](bool on){ if (on) { // TODO: 打开串口 } else { // TODO: 关闭串口 } }); }

浙公网安备 33010602011771号