赞
踩
有些时候我们使用QStackedWidget想要切换动画,就比如酷狗的这种效果:
使用自带的QStackedWidget是无法达到动画的切换效果的,所以为了满足这种需求我们可以继承QStackedWidget并结合使用QPropertyAnimation就可以达到这种效果。
我们知道,要切换QStackedWidget当前的显示界面可以调用QStackedWidget::setCurrentIndex()函数来切换,这种切换是跳跃式的,所以我们的动画就是在调用这个setCurrentIndex()函数之前开始,动画结束之后再调用setCurrentIndex()函数,这样就可以看到动画了。
但是这中间的动画过程要做什么事呢?那当然就是将两个Widget的过渡过程绘制出来了,在过渡完成之后再真正的切换QStackedWidget的显示界面。
如上图所示,线1的坐标就是QPropertyAnimation所计算出来的,线1
将整个QStackedWidget分成左、右两个部分,左边绘制widget1
(QStackedWidget中当前显示的Widget)的右半部分,右边绘制widget2
(QStackedWidget中将要显示的Widget)的左半部分,因为线1
是变化的,这就产生了我们看到的切换动画了
接着上面的原理,用代码来说明,在这之前一些知识点:
上面的两个函数说完了,感兴趣的可以自己去学习下绘图知识
下面看代码,首先是头文件:
class QAnimationStackedWidget : public QStackedWidget
{
Q_OBJECT
public:
explicit QAnimationStackedWidget(QWidget *parent = 0);
void paintEvent(QPaintEvent *);
//设置动画持续的间隔
void setDuration(int );
~QAnimationStackedWidget();
signals:
public slots:
//属性动画值改变的槽
void valueChanged_slot(QVariant );
//动画切换完成
void animationFinished();
//前一页
void next();
//下一页
void forward();
private:
void paintPrevious(QPainter &, int);
void paintNext(QPainter &, int);
private:
QPropertyAnimation *animation; //动画框架
int duration; //动画的持续时间
bool isAnimation; //是否正在动画
QVariant currentValue; //被Animation动画改变的值
int widgetCount; //保存当前StackWidget里面的子成员数
int nextIndex; //下一个要切换的索引
};
上面是自定义的QAnimationStackedWidget类,里面持有一个QPropertyAnimation对象,它的初始化部分:
QAnimationStackedWidget::QAnimationStackedWidget(QWidget *parent) :
QStackedWidget(parent)
{
isAnimation = false;
//设置默认的时间间隔
duration = 1000;
//初始化animation框架、并连接信号和槽
animation = new QPropertyAnimation(this, QByteArray());
connect(animation, SIGNAL(valueChanged(QVariant)), this, SLOT(valueChanged_slot(QVariant)));
connect(animation, SIGNAL(finished()), this, SLOT(animationFinished()));
}
然后声明一个切换下一页的槽函数:
void QAnimationStackedWidget::next()
{
//如果正在动画,那么return
if( isAnimation )
{
return;
}
isAnimation = true;
widgetCount = count();
int c = currentIndex();
//计算下一页的索引
nextIndex = (c + 1) % widgetCount;
//隐藏当前的widget
widget(c)->hide();
//开始动画并设置间隔和开始、结束值
QRect g = geometry();
int x = g.x();
int width = g.width();
animation->setStartValue(width);
animation->setEndValue(0);
animation->setDuration(duration);
animation->start();
}
上面的代码是开启动画,因为QPropertyAnimation::start()方法调用之后会发射valueChanged()信号,所以我们可以接收到改变了的值:
void QAnimationStackedWidget::valueChanged_slot(QVariant value)
{
currentValue = value;
update();
}
update又会触发paintEvent函数,所以我们在paintEvent里面可以将过渡的效果绘制出来:
void QAnimationStackedWidget::paintEvent(QPaintEvent *e)
{
if( isAnimation )
{
QPainter paint(this);
//绘制当前Widget
paintPrevious(paint, currentIndex());
//绘制下一个widget
paintNext(paint, nextIndex);
}
}
paintPrevious:
void QAnimationStackedWidget::paintPrevious(QPainter &paint, int currentIndex)
{
//获得当前页面的Widget
QWidget *w = widget(currentIndex);
QPixmap pixmap(w->size());
//将Widget的内容渲染到QPixmap对象中,即将Widget变成一张图片
w->render(&pixmap);
QRect r = w->geometry();
//绘制当前的Widget
double value = currentValue.toDouble();
QRectF r1(0.0, 0.0, value, r.height());
QRectF r2(r.width() - value, 0, value, r.height());
paint.drawPixmap(r1, pixmap, r2);
}
paintNext():
void QAnimationStackedWidget::paintNext(QPainter &paint, int nextIndex)
{
QWidget *nextWidget = widget(nextIndex);
QRect r = geometry();
//这行代码不加会有bug,第一次切换的时候,QStackedWidget并没有为child分配大小
nextWidget->resize(r.width(), r.height());
QPixmap nextPixmap(nextWidget->size());
nextWidget->render(&nextPixmap);
double value = currentValue.toDouble();
QRectF r1(value, 0.0, r.width() - value, r.height());
QRectF r2(0.0, 0.0, r.width() - value, r.height());
paint.drawPixmap(r1, nextPixmap, r2);
}
动画完成,真正开始切换界面:
void QAnimationStackedWidget::animationFinished()
{
isAnimation = false;
widget(currentIndex())->show();
setCurrentIndex(nextIndex);
}
至此,效果已经出来了,下面看看效果:
在实现这个效果的过程中有参考过这个博客:
源码地址:点我下载
Copyright © 2003-2013 www.wpsshop.cn 版权所有,并保留所有权利。