目录
前些阵子由于项目需求做了一个简单的日期选择器,但是个人觉得非常丑!!!之前做的一版是这个样子的:
后面根据项目的需求,并且实在是想换掉这个不好看的日期选择器,所以看了一些成熟公司的日期时间选择器案例,最后决定做成一个滚动式的,话不多说上代码:
1、.h文件代码:
#ifndef ROLLINGHOUR_H
#define ROLLINGHOUR_H
#include <QWidget>
#include <QMouseEvent>
#include <QPainter>
#include <QPropertyAnimation>
#include <QDateTime>
#include <QDebug>
class RollingHour : public QWidget
{
Q_OBJECT
/* 将采用动画的对象属性注册到QT中去,否则动画将无法执行 */
Q_PROPERTY(int posYShifting READ PosYShifting WRITE setPosYShifting)
public:
explicit RollingHour(QWidget *parent = nullptr);
~RollingHour();
/**
* @brief setHourRange 重新设置显示小时范围函数方法
* @param int hour最小值
* @param int hour最大值
* @return void 无
*/
void setHourRange(int min,int max); //设置重新设置范围
/**
* @brief PosYShifting 获取偏移量
* @return int posYShifting
*/
int PosYShifting();
/**
* @brief setPosYShifting 设置偏移量
* @param int 偏移量设置值
* @return void 无
*/
void setPosYShifting(int posYShifting);
protected:
/**
* @brief mousePressEvent 重写鼠标按压事件,鼠标拖动进行滚动
* @param QMouseEvent* 鼠标按压事件
* @return void 无
*/
void mousePressEvent(QMouseEvent *event) override;
/**
* @brief wheelEvent 重写滚轮事件,滚轮进行滚动
* @param QWheelEvent* 滚轮事件
* @return void 无
*/
void wheelEvent(QWheelEvent *event) override;
/**
* @brief mouseMoveEvent 重写鼠标移动事件,鼠标移动进入滚动判决
* @param QMouseEvent* 鼠标移动事件
* @return void 无
*/
void mouseMoveEvent(QMouseEvent *event) override;
/**
* @brief mouseReleaseEvent 重写鼠标松开事件,鼠标松开进入结果判决
* @param QMouseEvent* 鼠标松开事件
* @return void 无
*/
void mouseReleaseEvent(QMouseEvent *event) override;
/**
* @brief paintEvent 重写绘图事件,对hour进行绘制
* @param QPaintEvent* 绘图事件
* @return void 无
*/
void paintEvent(QPaintEvent *paint) override;
/**
* @brief drawNumber 画出滚动数字函数
* @param QPaintEvent* 画笔类导入
* @param int 数字值value
* @param int 滚动偏移量offset
* @return void 无
*/
void drawNumber(QPainter &painter,int value,int offset);
/**
* @brief rollAnimation 滚动动画方法
* @return void 无
*/
void rollAnimation();
private:
/* 小时的最小取值 */
int min_Range = 0;
/* 小时的最大取值 */
int max_Range = 24;
/* 字体显示大小 */
int numberSize = 5;
/* 鼠标移动时判决器 */
bool rollingJudgment = false;
/* 记录按压鼠标时Y方向的初始位置 */
int currentPosY = 0;
/* 取系统的小时时间作为滚动时间选择的初始值 */
int currentHour = QDateTime::currentDateTime().toString("hh").toInt();
/* 当下的Y方向上的偏移量 */
int posYShifting = 0;
/* 声明一个动画对象 */
QPropertyAnimation rollingAni;
};
#endif // ROLLINGHOUR_H
2、.cpp文件代码:
#include "rollinghour.h"
RollingHour::RollingHour(QWidget *parent)
:QWidget(parent)
{
/* 去边框,同时保留窗口原有的属性 */
this->setWindowFlags(Qt::FramelessWindowHint | windowFlags());;
/* 设置背景样式 */
this->setStyleSheet("background:white;");
/* 设置动画目标 */
rollingAni.setTargetObject(this);
/* 设置动画目标属性 */
rollingAni.setPropertyName("posYShifting");
/* 设置动画持续时间 */
rollingAni.setDuration(500);
/* 设置动画曲线样式 */
rollingAni.setEasingCurve(QEasingCurve::OutCirc);
}
RollingHour::~RollingHour()
{
}
void RollingHour::setHourRange(int min, int max)
{
min_Range = min;
max_Range = max;
update();
}
int RollingHour::PosYShifting()
{
/* 注册属性取值函数 */
return posYShifting;
}
void RollingHour::setPosYShifting(int posYShifting)
{
/* 注册属性赋值函数 */
this->posYShifting = posYShifting;
update();
}
void RollingHour::mousePressEvent(QMouseEvent *event)
{
rollingAni.stop();
/* 开启鼠标拖动滚动判决 */
rollingJudgment = true;
/* 刷新按下时鼠标位置 */
currentPosY = event->pos().y();
}
void RollingHour::wheelEvent(QWheelEvent *event)
{
/* 以滚轮delta值正负性作为判决条件进行判决 */
if(event->delta()>0)
{
if(currentHour > min_Range){
posYShifting = this->height()/4;
}
}
else{
if(currentHour < max_Range){
posYShifting = - this->height()/4;
}
}
rollAnimation();
update();
}
void RollingHour::mouseMoveEvent(QMouseEvent *event)
{
if(rollingJudgment)
{
/*边界判决,防止超出范围*/
if((currentHour == min_Range && event->pos().y() >= currentPosY)
||(currentHour == max_Range && event->pos().y() <= currentPosY)){
return;
}
else{
/*计算鼠标拖动时的垂直偏移量*/
posYShifting = event->pos().y() - currentPosY;
}
update();
}
}
void RollingHour::mouseReleaseEvent(QMouseEvent *event)
{
Q_UNUSED(event);
/*拖动判决归位*/
if(rollingJudgment)
{
rollingJudgment = false;
/* 开启动画 */
rollAnimation();
}
}
void RollingHour::paintEvent(QPaintEvent *paint)
{
Q_UNUSED(paint);
/* 创建画笔对象 */
QPainter painter(this);
/* 画笔抗锯齿操作 */
painter.setRenderHint(QPainter::TextAntialiasing);
painter.setRenderHint(QPainter::Antialiasing, true);
/* 偏移量检测,以1/4高度和取值范围为边界 */
if(posYShifting >= height() / 4 && currentHour > min_Range)
{
/* 鼠标起始位置归位,即加上1/4的高度 */
currentPosY += height()/4;
/* 偏移量归位,即减去1/4的高度 */
posYShifting -= height()/4;
/* currentHour自减 */
currentHour -= 1;
}
if(posYShifting <= -height() / 4 && currentHour < max_Range)
{
currentPosY -= height() / 4;
posYShifting += height() / 4;
currentHour += 1;
}
/* 调用函数画出数字currentHour */
drawNumber(painter,currentHour,posYShifting);
/* 调用函数画出两侧数字 */
if(currentHour != min_Range){
drawNumber(painter,currentHour - 1,posYShifting - height() / 4);
}
if(currentHour != max_Range){
drawNumber(painter,currentHour + 1,posYShifting + height() / 4);
}
/* 调用函数画出两侧数字 */
if(posYShifting >= 0 && currentHour - 2 >= min_Range){
drawNumber(painter,currentHour - 2,posYShifting - height() / 2);
}
if(posYShifting <= 0 && currentHour + 2 <= max_Range){
drawNumber(painter,currentHour + 2,posYShifting + height() / 2);
}
/* 画出数字currentHour两侧边框 */
painter.setPen(QPen(QColor(70,144,249),2));
painter.drawLine(0,height() / 8 * 3,width(),height() / 8 * 3);
painter.drawLine(0,height() / 8 * 5,width(),height() / 8 * 5);
}
void RollingHour::drawNumber(QPainter &painter, int value, int offset)
{
/* 通过偏移量控制数字大小size */
int size = (this->height() - abs(offset)) / numberSize;
/* 通过偏移量控制数字透明度transparency */
int transparency = 255 - 510 * abs(offset) / height();
/* 通过偏移量控制数字在ui界面占据空间高度height */
int height = this->height() / 2 - 3 * abs(offset) / 5;
/* 计算数字显示位置 */
int y = this->height() / 2 + offset - height / 2;
QFont font;
/* 设置字体大小 */
font.setPixelSize(size);
/* 画笔设置字体 */
painter.setFont(font);
/* 画笔设置颜色 */
painter.setPen(QColor(0,0,0,transparency));
/* 画笔painter画出文本*/
painter.drawText(QRectF(0,y,width(),height),Qt::AlignCenter,(QString::number(value)+tr("时")));
}
void RollingHour::rollAnimation()
{
/* 动画判决,达到条件开启相应动画 */
if(posYShifting > height() / 8)
{
/* 设置属性动画初始value */
rollingAni.setStartValue(height() / 8 - posYShifting);
/* 设置属性动画终止value */
rollingAni.setEndValue(0);
/* currentHour值变更 */
currentHour--;
}
else if(posYShifting > -height() / 8)
{
/* 设置属性动画初始value */
rollingAni.setStartValue(posYShifting);
/* 设置属性动画终止value */
rollingAni.setEndValue(0);
}
else if(posYShifting < -height() / 8)
{
/* 设置属性动画初始value */
rollingAni.setStartValue(-height() / 8 - posYShifting);
/* 设置属性动画终止value */
rollingAni.setEndValue(0);
/* currentHour值变更 */
currentHour++;
}
/* 动画开始 */
rollingAni.start();
update();
}
3、滚动日期时间选择器的基本原理和实现思路:
该方法主要是通过QPainter画笔类将想要的数字画出来的(包括三组数字:显示数字;显示数字的上一个数字;显示数字的下一个数字),除此之外在滚动时数字还应该有大小和对应位置的变化过程,在这里就应该引入一个动画,通过动画曲线来达到一个比较好的效果。
动画则需要针对一个对象的具体属性作为动画目标来进行的,所以在这里我采用偏移量属性来实现滚动时的动画效果,并且通过偏移量来判断滚动的结果以及滚动时的数字字体大小。在整个代码中我也对每句话都做了注解,有需要学习的小伙伴可以自行看一下。
需要注意的是针对不是Object的基本属性(类如:pos()、size()、width()等基本属性)时,我们需要将其注册到QT中去,否则这个属性是无法运用动画的。
最后,通过封装和实例化对象,可以将这个滚动选择封装成一个类,从而实现一个随取随用的自定义控件,以后不论在什么项目中都可以采用这个做法去实现一个日期时间选择器。
4、实现效果:
整个自定义控件的实现效果是这样的:
对于最后的整体实现效果,如果有需要可以看我主页哦!!!