最近因为在玩iOS手机,突然觉得人家的开关就是很好看,于是乎自己花了点时间写了一下,下面是效果图,代码中没有使用定时器,而是直接用的属性动画,并且支持横向和竖向动态缩放。

实现代码如下:
Switch.h
#ifndef WIDGET_H
#define WIDGET_H
#include <QVariant>
#include <QWidget>
enum class AnimationType
{
NoAnimation, //没有动画
PropertyAnimation, //属性动画
};
class QPropertyAnimation;
class Switch : public QWidget
{
Q_OBJECT
public:
Switch(QWidget *parent = nullptr);
~Switch();
protected:
virtual void paintEvent(QPaintEvent *e) override;
virtual void resizeEvent(QResizeEvent *e) override;
virtual void mousePressEvent(QMouseEvent *e) override;
private slots:
void OnValueChanged(QVariant);
private:
void CalcSliderPos();
void PaintLeftArea(QPainter&,int);
void PaintRightArea(QPainter&,int);
void PaintSliderArea(QPainter&,int);
private:
bool m_IsOpen; //当前状态
int m_SliderPos; //滑块当前位置
QColor m_CloseBgColor; //关闭后背景颜色
QColor m_OpenBgColor; //打开后背景颜色
QColor m_SliderBgColor; //滑块背景颜色
AnimationType m_AnimationType; //动画类型
QPropertyAnimation* m_PropertyAnimation;
};
#endif // WIDGET_H
Swtich.cpp
#include "Switch.h"
#include <QPainter>
#include <QMouseEvent>
#include <QPropertyAnimation>
Switch:: Switch(QWidget *parent)
: QWidget(parent)
, m_IsOpen(false)
, m_SliderPos(0)
, m_CloseBgColor(Qt::gray)
, m_OpenBgColor(Qt::green)
, m_SliderBgColor(Qt::white)
, m_AnimationType(AnimationType::PropertyAnimation)
, m_PropertyAnimation(Q_NULLPTR)
{
switch(m_AnimationType)
{
case AnimationType::NoAnimation:
break;
case AnimationType::PropertyAnimation:
m_PropertyAnimation = new QPropertyAnimation(this,"");
connect(m_PropertyAnimation,&QPropertyAnimation::valueChanged,this,&Switch::OnValueChanged);
break;
default:
break;
}
}
Switch::~ Switch()
{}
void Switch::CalcSliderPos()
{
int size=qMin(width(),height());
if(m_IsOpen){
if(width()<height()){
m_SliderPos = this->height()-size;
}
else{
m_SliderPos = width()-size;
}
}
else{
m_SliderPos = 0;
}
}
void Switch::resizeEvent(QResizeEvent *e)
{
CalcSliderPos();
QWidget::resizeEvent(e);
}
void Switch::mousePressEvent(QMouseEvent *e)
{
if(Qt::LeftButton==e->button()){
m_IsOpen=!m_IsOpen;
switch(m_AnimationType)
{
case AnimationType::NoAnimation:
CalcSliderPos();
update();
break;
case AnimationType::PropertyAnimation:
if(Q_NULLPTR==m_PropertyAnimation){
CalcSliderPos();
update();
}
else{
m_PropertyAnimation->stop();
if(m_IsOpen){
m_PropertyAnimation->setStartValue(0);
m_PropertyAnimation->setEndValue(qAbs(width()-height()));
}
else{
m_PropertyAnimation->setStartValue (qAbs(width()-height()));
m_PropertyAnimation->setEndValue(0);
}
m_PropertyAnimation->setDuration(200);
m_PropertyAnimation->start();
}
break;
default:
break;
}
}
}
void Switch::OnValueChanged(QVariant value)
{
m_SliderPos=value.toInt();
update();
}
void Switch::paintEvent(QPaintEvent *e)
{
QPainter painter(this);
//设置反走样
painter.setRenderHint(QPainter::Antialiasing, true);
int size=qMin(width(),height());
PaintLeftArea(painter,size);
PaintRightArea(painter,size);
PaintSliderArea(painter,size);
QWidget::paintEvent(e);
}
void Switch::PaintLeftArea(QPainter& painter,int size)
{
painter.save();
//计算左边外框线
QPainterPath painterPath;
painterPath.addEllipse(0,0,size,size);
if(width()<height()){
QPainterPath rectPath;
rectPath.addRect(0,size/2,size,m_SliderPos);
QPainterPath ellipsePath;
ellipsePath.addEllipse(0,m_SliderPos,size,size);
ellipsePath=ellipsePath.united(rectPath);
painterPath=painterPath.united(ellipsePath);
}
else{
QPainterPath rectPath;
rectPath.addRect(size/2,0,m_SliderPos,size);
QPainterPath ellipsePath;
ellipsePath.addEllipse(m_SliderPos,0,size,size);
ellipsePath=ellipsePath.united(rectPath);
painterPath=painterPath.united(ellipsePath);
}
//绘制左边外框-并填充背景色
painter.setPen(Qt::NoPen);
if(m_OpenBgColor.isValid()){
painter.setBrush(Qt::green);
}
else{
painter.setBrush(QColor(m_OpenBgColor));
}
painter.drawPath(painterPath);
painter.restore();
}
void Switch::PaintRightArea(QPainter &painter,int size)
{
painter.save();
//计算右边外框线
QPainterPath painterPath;
if(width()<height()){
painterPath.addEllipse(0,height()-size,size,size);
QPainterPath ellipsePath;
ellipsePath.addEllipse(0,m_SliderPos,size,size);
QPainterPath rectPath;
rectPath.addRect(0,m_SliderPos+size/2,size,height()-m_SliderPos-size);
ellipsePath=ellipsePath.united(rectPath);
painterPath=painterPath.united(ellipsePath);
}
else{
painterPath.addEllipse(width()-size,0,size,size);
QPainterPath ellipsePath;
ellipsePath.addEllipse(m_SliderPos,0,size,size);
QPainterPath rectPath;
rectPath.addRect(m_SliderPos+size/2,0,width()-m_SliderPos-size,size);
ellipsePath=ellipsePath.united(rectPath);
painterPath=painterPath.united(ellipsePath);
}
//绘制左边外框-并填充背景色
painter.setPen(Qt::NoPen);
if(m_CloseBgColor.isValid()){
painter.setBrush(Qt::gray);
}
else{
painter.setBrush(QColor(m_CloseBgColor));
}
painter.drawPath(painterPath);
painter.restore();
}
void Switch::PaintSliderArea(QPainter &painter,int size)
{
painter.save();
//绘制滑块-并填充颜色
painter.setPen(Qt::NoPen);
if(m_SliderBgColor.isValid()){
painter.setBrush(Qt::white);
}
else{
painter.setBrush(QColor(m_SliderBgColor));
}
if(width()<height()){
painter.drawEllipse(0,m_SliderPos,size,size);
}
else{
painter.drawEllipse(m_SliderPos,0,size,size);
}
painter.restore();
}
这里的私有变量可以通过共有接口设置以供外部调用设置颜色等。










