最近arm-linux环境设备机中应用软件需要做日期时间选择面板,要求做成滚动日期选择的样式如下图一,网上查找资料找到了一篇实现数字滚动选择的博客,对其进行修改补充实现了以下功能,本期只介绍数字滚动的实现,下期分享图一的实现。Qt 滚动选择学习_lg15273112290的博客-CSDN博客[参考链接](https://blog.csdn.net/BIG_C_GOD/article/details/52452631?ops_request_misc=%257B%2522request%255Fid%2522%253A%2522161648329416780271563953%2522%252C%2522scm%2522%253A%252220140713.130102334.pc%255Fall.%2522%257D&request_id=1616483294167802715639https://blog.csdn.net/lg15273112290/article/details/115124797
图一 滚动日期选择
复现以上博主的代码时发现滑动太快有不居中的情况,如下图
调试代码发现在数字矫正函数homing()里加上这句m_deviation=0;就可以使数字居中,以下为相关代码:
头文件中为:
#ifndef TIMESELECT_H #define TIMESELECT_H #include <QWidget> #include <QPropertyAnimation> #include <QPainter> #include <QMouseEvent> #include <QDebug> class timeSelect : public QWidget { Q_OBJECT public: explicit timeSelect(QWidget *parent = nullptr); ~timeSelect(); public: void setRange(int min, int max);//设置范围 int readValue(); //获取当前值 void setFormat(QString prefix,QString suffix);//设定显示格式,前缀和后缀 void setCurValue(int Value); protected: void mousePressEvent(QMouseEvent *event); void mouseMoveEvent(QMouseEvent *event); void mouseReleaseEvent(QMouseEvent *event); void paintEvent(QPaintEvent * m_painter); void paintNum(QPainter &painter,int num,int deviation,bool isMiddle); //绘制数字 void homing(); //使选中的数字回到屏幕中间 int readDeviation(); //获取鼠标移动偏移量,默认为0 void setDeviation(int n); //设置偏移量 private: int m_minRange; //最小值 int m_maxRange; //最大值 int m_currentValue; //当前选中的值 bool isDragging; //鼠标是否按下 int m_deviation; //偏移量,记录鼠标按下后移动的垂直距离 int m_mouseSrcPos; //鼠标源X轴作标,用来计算偏移量 int m_numSize; //计算所得的数字字符尺寸,以最长的情况为准 bool isMiddle; //是否是中间的数字 QString m_prefix;//显示格式前缀 QString m_suffix;//显示格式后缀 QPropertyAnimation *homingAni; //QPropertyAnimation 是一个控制动画效果的类 signals: void currentValueChanged(int value); //当前数值改变信号 void deviationChange(float deviation); //偏移量改变信号 public slots: }; #endif // TIMESELECT_H
具体函数文件.cpp的实现:
#include "timeselect.h" timeSelect::timeSelect(QWidget *parent) : QWidget(parent),m_minRange(1),m_maxRange(50), m_currentValue(16),isDragging(0),m_deviation(0), m_numSize(6),isMiddle(false),m_prefix(""),m_suffix("") { homingAni = new QPropertyAnimation(this,""); //创建一个动画类 homingAni->setDuration(300); //设置动画持续时间 homingAni->setEasingCurve(QEasingCurve::OutQuad); //设置动画的缓动曲线,这里OutQuad是二次函数缓和曲线,减速到零速度 } timeSelect::~timeSelect() { } //设置范围 void timeSelect::setRange(int min, int max) { m_minRange = min; m_maxRange = max; if(m_currentValue < min) //如果选中的当前值小于最小值,就等于最小值,不会再减了 { m_currentValue = min; } if(m_currentValue > max) //最大值同理 { m_currentValue = max; } //计算字符尺寸 m_numSize = 3; int temp = m_maxRange; while(temp > 0) { temp /= 10; m_numSize++; } update(); //重绘此组件,需要进行重绘时,要使用update } //获取当前值 int timeSelect::readValue() { return m_currentValue; } //设置显示格式 void timeSelect::setFormat(QString prefix, QString suffix) { m_prefix=prefix; m_suffix=suffix; } //设置当前显示 void timeSelect::setCurValue(int Value) { //判断是否在有效区间内 if(Value > m_maxRange) { Value=m_maxRange; } else if(Value < m_minRange) { Value=m_minRange; } m_currentValue=Value; if(this->isVisible()) { update(); } } //鼠标按下事件 void timeSelect::mousePressEvent(QMouseEvent *event) { homingAni->stop(); //动画停止 isDragging = true; //鼠标是否按下的标志位,布尔类型 m_mouseSrcPos = event->pos().y(); //鼠标按下时候的y轴作标记录下来 QWidget::mousePressEvent(event); } //鼠标移动事件 void timeSelect::mouseMoveEvent(QMouseEvent *event) { if(isDragging) { //数值到边界时,阻止继续往对应方向移动 if((m_currentValue == m_minRange && event->pos().y() >= m_mouseSrcPos)|| (m_currentValue == m_maxRange && event->pos().y() <= m_mouseSrcPos)) return; m_deviation = event->pos().y() - m_mouseSrcPos; //垂直偏移量 = 现在的坐标 - 开始的作标 //若移动速度过快时进行限制 if(m_deviation > (height()-1)/5) { m_deviation = (height()-1)/5; } else if(m_deviation < -(height()-1)/5) { m_deviation = -(height()-1)/5; } emit deviationChange((float)m_deviation/((height()-1)/5)); //发出偏移量改变的信号,随着偏移量改变,重绘数字,就变成数字滚动的效果 update(); } QWidget::mouseMoveEvent(event); } //鼠标松开事件 void timeSelect::mouseReleaseEvent(QMouseEvent *event) { if(isDragging) //如果鼠标松开了,那么还原一下鼠标按下标志位 { homing(); //使选中的数字回到屏幕中间 isDragging = false; update(); } QWidget::mouseReleaseEvent(event); } //绘图事件 void timeSelect::paintEvent(QPaintEvent *m_painter) { QPainter painter(this); //创建画家类,指定绘图设备,也就是在哪画 painter.setRenderHint(QPainter::Antialiasing, true); //反走样,就是抗锯齿 int Width = width()-1; //设置宽高 int Height = height()-1; if(m_deviation >= Height/5 && m_currentValue > m_minRange) //偏移量大于1/4 高的时候,数字减一 { m_mouseSrcPos += Height/5; //鼠标起始位置重新设置,即加上1/4的高度 m_deviation -= Height/5; //偏移量重新设置,即减去1/4的高度 m_currentValue--; } if(m_deviation <= -Height/5 && m_currentValue < m_maxRange) //同理,数字加一 { m_mouseSrcPos -= Height/5; m_deviation += Height/5; m_currentValue++; } //中间数字 paintNum(painter,m_currentValue,m_deviation,isMiddle=1); //将选中数字画到中间 //两侧数字1 if(m_currentValue != m_minRange) //选中的数字不是最小,不是最大,那么就有两侧数字,然后画出两侧数字 paintNum(painter,m_currentValue-1,m_deviation-Height*2/10,isMiddle=0); if(m_currentValue != m_maxRange) paintNum(painter,m_currentValue+1,m_deviation+Height*2/10,isMiddle=0); //两侧数字2,超出则不显示 if(m_deviation >= 0 && m_currentValue-2 >= m_minRange) paintNum(painter,m_currentValue-2,m_deviation-Height*4/10,isMiddle=0); if(m_deviation <= 0 && m_currentValue+2 <= m_maxRange) paintNum(painter,m_currentValue+2,m_deviation+Height*4/10,isMiddle=0); //画边框,中间数字两侧的边框 painter.setPen(QPen(QColor(5,39,175,120),4)); painter.drawLine(0,Height/8*3,Width,Height/8*3); painter.drawLine(0,Height/8*5,Width,Height/8*5); QWidget::paintEvent(m_painter); } //画数字函数 void timeSelect::paintNum(QPainter &painter, int num, int deviation, bool isMiddle) { int Width = this->width()-1; int Height = this->height()-1; int size = (Height - qAbs(deviation))/m_numSize; //qAbs 返回输入参数对应类型的绝对值。 int transparency = 255-510*qAbs(deviation)/Height; //设置透明度 int height = Height/2-3*qAbs(deviation)/5; int y = Height/2+deviation-height/2; QFont font; font.setPixelSize(size); //设置像素大小 painter.setFont(font); //设置字体 if(isMiddle) { painter.setPen(QColor(34,215,187,transparency)); //设置画笔,基色 } else { painter.setPen(QColor(0,0,0,transparency)); //设置画笔,黑色 } QString str_date; if(num<=9) { str_date=QString("0")+QString::number(num); } else { str_date=QString::number(num); } str_date=m_prefix+str_date+m_suffix; painter.drawText(QRectF(0,y,Width,height), //画文本,参数:QRectF参数:位置xy,长宽大小;对齐方式,中间对齐;内容 Qt::AlignCenter, str_date); } //使选中的数字回到屏幕中间 void timeSelect::homing() { //将数字矫正到中心 if(m_deviation > height()/20) { homingAni->setStartValue((height()-1)/8-m_deviation); homingAni->setEndValue(0); m_currentValue--; } else if(m_deviation > -(height())/20) { homingAni->setStartValue(m_deviation); homingAni->setEndValue(0); } else if(m_deviation < -(height())/20) { homingAni->setStartValue(-(height()-1)/8-m_deviation); homingAni->setEndValue(0); m_currentValue++; } m_deviation=0; emit currentValueChanged(m_currentValue); //发送当前数值改变信号 homingAni->start(); //开始动画 } //获取鼠标移动偏移量 int timeSelect::readDeviation() { return m_deviation; } //设置偏移量 void timeSelect::setDeviation(int n) { m_deviation = n; update(); }
最终实现的效果如下图,增加了几个显示接口,并使数字居中显示: