代码:
#ifndef CLICKTOSWIPEWIDGET_H
#define CLICKTOSWIPEWIDGET_H
#include <QWidget>
#include <QTimer>
class ClickToSwipeWidget : public QWidget
{
Q_OBJECT
public:
ClickToSwipeWidget(QWidget *parent = nullptr);
~ClickToSwipeWidget();
signals:
void clicked(int index);
protected:
void paintEvent(QPaintEvent *event)override;
QSize sizeHint() const override;
void mousePressEvent(QMouseEvent *event)override;
void mouseReleaseEvent(QMouseEvent *event)override;
private:
enum class state
{
UnPressed,//未按下
StretchingWidth_addWidth,//正在伸长宽度
iconShowing,//图标正在显示
iconShowed,//图标已显示
iconHiding,//图标正在隐藏
StretchingWidth_reduce//正在缩短宽度
};
state widgetState{state::UnPressed};
QList<QPixmap> imgList;
QList<QRect> imgRect;
int widthChangeValue{0};//宽度变化值
QTimer timer;
void onTimer();
int angle{0};
int pressIconIndex{-1};//按下的图标的索引
};
#endif // CLICKTOSWIPEWIDGET_H
#include "clicktoswipewidget.h"
#include <QPainter>
#include <QPaintEvent>
#include <QPainterPath>
const int normalStatusSize = 60;
const int iconSize = 28;//图标宽度
const int iconShowDstY = (normalStatusSize - iconSize) / 2;//图标显示最终位置的Y值 16 = (60 - 图标高度28)/ 2
const int iconHideY = normalStatusSize + 10;//图标隐藏最终位置的Y值
ClickToSwipeWidget::ClickToSwipeWidget(QWidget *parent)
: QWidget(parent)
{
this->setWindowFlags(Qt::FramelessWindowHint);
this->setAttribute(Qt::WA_TranslucentBackground, true);
setSizePolicy(QSizePolicy::Fixed,QSizePolicy::Fixed);
connect(&timer,&QTimer::timeout,this,&ClickToSwipeWidget::onTimer);
timer.setInterval(40);
imgList << QPixmap(":/img/1.png");
imgList << QPixmap(":/img/1p.png");
imgList << QPixmap(":/img/2.png");
imgList << QPixmap(":/img/2p.png");
imgList << QPixmap(":/img/3.png");
imgList << QPixmap(":/img/3p.png");
assert((!imgList.isEmpty()) && (imgList.size() % 2 == 0));
for(int i = 0;i < imgList.size() / 2;++i)
{
imgRect << QRect(normalStatusSize + i * (normalStatusSize - 10) + (normalStatusSize - 10 - iconSize) / 2,
iconHideY,iconSize,iconSize);
}
}
ClickToSwipeWidget::~ClickToSwipeWidget()
{
}
void ClickToSwipeWidget::paintEvent(QPaintEvent *event)
{
QPainter painter(this);
painter.setRenderHint(QPainter::Antialiasing,true);
painter.setRenderHint(QPainter::SmoothPixmapTransform,true);
auto rect = event->rect();
QPainterPath path;
path.addRoundedRect(rect.adjusted(5,5,-5,-5),normalStatusSize / 2,normalStatusSize / 2);
painter.setClipPath(path);
painter.fillRect(rect,Qt::white);
QPen pen(QColor(0x22a74c),5,Qt::SolidLine,Qt::RoundCap,Qt::RoundJoin);
painter.setPen(pen);
painter.drawRoundedRect(rect.adjusted(5,5,-5,-5),normalStatusSize / 2,normalStatusSize / 2);
auto radiu = normalStatusSize / 2 - 6;
painter.save();
painter.drawRoundedRect(rect.adjusted(5,5,-5,-5),normalStatusSize / 2,normalStatusSize / 2);
painter.translate(QPoint(normalStatusSize / 2,normalStatusSize / 2));
auto lineWidth = radiu/3.0f;
if(widgetState == state::StretchingWidth_addWidth ||
widgetState == state::iconShowing ||
widgetState == state::iconShowed ||
widgetState == state::iconHiding ||
widgetState == state::StretchingWidth_reduce)
{
painter.rotate(angle);
}
painter.drawLine(QPoint(-lineWidth,0),QPoint(lineWidth,0));
painter.drawLine(QPoint(0,-lineWidth),QPoint(0,lineWidth));
painter.restore();
if(widgetState == state::iconShowing ||
widgetState == state::iconShowed ||
widgetState == state::iconHiding)
{
for(int i = 0;i < imgRect.size();++i)
{
painter.drawPixmap(imgRect.at(i),imgList.at(i*2));
}
}
if(widgetState == state::iconShowed && pressIconIndex != -1)
{
painter.drawPixmap(imgRect.at(pressIconIndex),imgList.at(pressIconIndex*2+1));
}
}
QSize ClickToSwipeWidget::sizeHint() const
{
if(widgetState == state::UnPressed)
{
return QSize(normalStatusSize, normalStatusSize);
}
else
{
return QSize(normalStatusSize + widthChangeValue, normalStatusSize);
}
}
void ClickToSwipeWidget::mousePressEvent(QMouseEvent *event)
{
if(widgetState == state::UnPressed)
{
widgetState = state::StretchingWidth_addWidth;
widthChangeValue = 0;
angle = 0;
timer.start();
}
else if(widgetState == state::iconShowed)
{
auto pos = event->pos();
for (int var = 0; var < imgRect.size(); ++var)
{
if(imgRect.at(var).contains(pos))
{
pressIconIndex = var;
break;
}
}
if(pressIconIndex != -1)
{
update();
emit clicked(pressIconIndex);
}
else
{
if(QRect(10,10,normalStatusSize - 20,normalStatusSize - 20).contains(pos))
{
widgetState = state::iconHiding;
timer.start();
}
}
}
}
void ClickToSwipeWidget::mouseReleaseEvent(QMouseEvent *event)
{
if(widgetState == state::iconShowed)
{
if(pressIconIndex != -1)
{
pressIconIndex = -1;
update();
}
}
}
void ClickToSwipeWidget::onTimer()
{
if(widgetState == state::StretchingWidth_addWidth)
{
auto thisIsInLayout = testAttribute(Qt::WA_LaidOut);//控件是否在布局中
if(thisIsInLayout)
setSizePolicy(QSizePolicy::Minimum,QSizePolicy::Fixed);//Minimum:sizeHint() 提供的大小为最小大小,可拉伸(布局中)
angle += 5;
auto temp = (imgList.size() / 2 * (normalStatusSize - 10));
widthChangeValue += (temp / 10);
if(widthChangeValue > temp)
{
widgetState = state::iconShowing;
}
if(!thisIsInLayout)
adjustSize();//控件不在布局中时调整大小(不在布局中)
update();
if(thisIsInLayout)
setSizePolicy(QSizePolicy::Fixed,QSizePolicy::Fixed);
}
else if(widgetState == state::iconShowing)
{
bool 所有图标都垂直居中{true};
for(int i = 0;i < imgRect.size();++i)
{
auto rect = imgRect[i];
auto y = rect.y();
if(y == iconShowDstY)
{
continue;
}
所有图标都垂直居中 = false;
y -= 7;
if(y < iconShowDstY)
{
rect.moveTopLeft(QPoint(rect.x(),iconShowDstY));
}
else
{
rect.moveTop(y);
}
imgRect[i] = rect;
break;
}
if(所有图标都垂直居中)
{
widgetState = state::iconShowed;
timer.stop();
}
update();
}
else if(widgetState == state::iconHiding)
{
bool 所有图标都隐藏了{true};
for(int i = imgRect.size() - 1;i >= 0;--i)
{
auto rect = imgRect[i];
auto y = rect.y();
if(y == iconHideY)
{
continue;
}
所有图标都隐藏了 = false;
y += 7;
if(y >= iconHideY)
{
rect.moveTopLeft(QPoint(rect.x(),iconHideY));
}
else
{
rect.moveTop(y);
}
imgRect[i] = rect;
break;
}
if(所有图标都隐藏了)
{
widgetState = state::StretchingWidth_reduce;
}
update();
}
else if(widgetState == state::StretchingWidth_reduce)
{
auto thisIsInLayout = testAttribute(Qt::WA_LaidOut);
if(thisIsInLayout)
setSizePolicy(QSizePolicy::Minimum,QSizePolicy::Fixed);
angle -= 5;
auto temp = (imgList.size() / 2 * (normalStatusSize - 10));
widthChangeValue -= (temp / 10);
if(widthChangeValue <= 0)
{
widgetState = state::UnPressed;
timer.stop();
}
if(!thisIsInLayout)
adjustSize();//控件不在布局中时调整大小(不在布局中)
update();
if(thisIsInLayout)
setSizePolicy(QSizePolicy::Fixed,QSizePolicy::Fixed);
}
}
使用:
#include "clicktoswipewidget.h"
#include <QApplication>
#include <QHBoxLayout>
#include <QDebug>
int main(int argc, char *argv[])
{
QApplication a(argc, argv);
QWidget w;
auto btn = new ClickToSwipeWidget;
auto hb = new QHBoxLayout(&w);
hb->setSizeConstraint(QLayout::SetMinimumSize);
hb->addStretch();
hb->addWidget(btn);
hb->addStretch();
w.resize(800,400);
w.show();
QWidget::connect(btn,&ClickToSwipeWidget::clicked,[](int index)
{
if(index == 0)
qDebug()<<"点赞";
else if(index == 1)
qDebug()<<"投币";
else if(index == 2)
qDebug()<<"收藏";
});
return a.exec();
}
控件的宽度和添加的图标个数有关,如只添加两组图标:
imgList << QPixmap(":/img/1.png");
imgList << QPixmap(":/img/1p.png");
imgList << QPixmap(":/img/2.png");
imgList << QPixmap(":/img/2p.png");
// imgList << QPixmap(":/img/3.png");
// imgList << QPixmap(":/img/3p.png");
要改变控件尺寸只需要配置这两个量:
如设置:
const int normalStatusSize = 100;
const int iconSize = 40;//图标宽度
这个控件适合单独使用悬浮在窗口的一侧。
UI参考:jQuery点击滑动显示分享按钮代码