一、前言
这是基于Qt实现的自定义滑动选择器,滑动选择器支持两种模式:水平模式和垂直模式,可自行设置。另外提供了接口可设置各类颜色、选择器值域等。
在这里提供了下载链接。
二、效果展示
三、控件分析
滑动选择器,是一个可以在通过控件上左右滑动,然后左右两侧数据会随之左右滚动并标记(颜色突出)出当前值,来进行选择值的一个控件。我们可以把它拆分为三部分:背景框、分隔线、数字,所以我们可以把绘制过程分为三部分,绘制背景框、绘制分割线、绘制数字。
四、原理详解
在绘制完画面之后,怎么来实现它的滑动是本控件的关键之处。由于本滑动选择器支持水平模式和垂直模式,在此以水平模式为例进行讲解。要实现滑动,我们需要重写鼠标点击和鼠标移动事件。在鼠标点下时,记录鼠标当前点击处的x座标(垂直模式则记录鼠标当前点击处的y座标)。当鼠标移动时,我们先要判断当前值是否超过了值域范围。当前值等于最小值了并且鼠标还在往右边滑动,则return;当前值等于最大值并且鼠标还在往左滑,则return。然后我们需要实时计算出鼠标移动的距离,最后更新绘图。更新绘图时,显然背景框和分割线是不会发生改变的,只有数字会发送改变。所以只要绘制数字的矩形框的横座标跟我们的鼠标移动距离进行绑定(数字的绘制会参考鼠标的移动距离),那么我们滑动,字体就会随之滑动。
按造以上已经基本能实现简易的滑动选择器,但是它还不够丝滑(当鼠标移动完释放,数字移动会立刻停止,可能下一个数字还没到当前位置就停止了,然后就会出现两个数字同框的局面),所以我们要想办法,让它在鼠标释放时根据移动距离进行数据矫正,把数值矫正到中心位置。要实现这个功能,需要使用到QPropertyAnimation类,首先我们给类添加一个属性,将鼠标移动距离这个值暴露出来,然后将这个属性名与QPropertyAnimation绑定。如果当前值向右移动超过了半个字,则切换到下一个字;如果未超过则当前值直接回滚到中心位置;如果当前值向左移动超过了半个字,则切换到上一个字。
五、关键代码
void SlidingSelector::paintEvent(QPaintEvent *)
{
QPainter painter(this);
painter.setRenderHint(QPainter::Antialiasing, true);
paint_frame(&painter);
paint_parting_line(&painter);
int Width = width()-1;
int Height = height()-1;
if(mode == Horizontal)
{
if(mouse_move_length >= Width/4 && Current_num > MIN_NUM)
{
mouse_Press += Width/4;
mouse_move_length -= Width/4;
Current_num -= 1;
}
if(mouse_move_length <= -Width/4 && Current_num < MAX_NUM)
{
mouse_Press -= Width/4;
mouse_move_length += Width/4;
Current_num += 1;
}
//中间数字
paint_num(&painter,Current_num,mouse_move_length,1);
//两侧数字1
if(Current_num != MIN_NUM)
paint_num(&painter,Current_num-1,mouse_move_length-Width/4,0);
if(Current_num != MAX_NUM)
paint_num(&painter,Current_num+1,mouse_move_length+Width/4,0);
//两侧数字2,超出则不显示
if(mouse_move_length >= 0 && Current_num-2 >= MIN_NUM)
paint_num(&painter,Current_num-2,mouse_move_length-Width/2,0);
if(mouse_move_length <= 0 && Current_num+2 <= MAX_NUM)
paint_num(&painter,Current_num+2,mouse_move_length+Width/2,0);
}
else
{
if(mouse_move_length >= Height/4 && Current_num > MIN_NUM)
{
mouse_Press += Height/4;
mouse_move_length -= Height/4;
Current_num -= 1;
}
if(mouse_move_length <= -Height/4 && Current_num < MAX_NUM)
{
mouse_Press -= Height/4;
mouse_move_length += Height/4;
Current_num += 1;
}
//中间数字
paint_num(&painter,Current_num,mouse_move_length,1);
//上下数字1
if(Current_num != MIN_NUM)
paint_num(&painter,Current_num-1,mouse_move_length-Height/4,0);
if(Current_num != MAX_NUM)
paint_num(&painter,Current_num+1,mouse_move_length+Height/4,0);
//上下数字2,超出则不显示
if(mouse_move_length >= 0 && Current_num-2 >= MIN_NUM)
paint_num(&painter,Current_num-2,mouse_move_length-Height/2,0);
if(mouse_move_length <= 0 && Current_num+2 <= MAX_NUM)
paint_num(&painter,Current_num+2,mouse_move_length+Height/2,0);
}
}
void SlidingSelector::paint_frame(QPainter *painter)
{
painter->save();
painter->setPen(Qt::NoPen);
painter->setBrush(frame_color);
painter->drawRect(space,space,width()-space*2,height()-space*2);
painter->restore();
}
void SlidingSelector::paint_num(QPainter *painter,int num,int mouse_move_length,int mid)
{
painter->save();
int Width = width()-1;
int Height = height()-1;
if(mode == Horizontal)
{
int size = qAbs((Width - qAbs(mouse_move_length))/num_size);//像素尺寸
int width = Width/2-3*qAbs(mouse_move_length);//数字框宽度
int x = Width/2+mouse_move_length-width/2;//数字框x座标
QFont font;
font.setPixelSize(size);
painter->setFont(font);
if(mid == 1)
painter->setPen(currentNum_color);
else
painter->setPen(besideNum_color);
painter->drawText(QRectF(x,0,width,Height),Qt::AlignCenter,QString::number(num));
}
else
{
int size = qAbs((Height - qAbs(mouse_move_length))/num_size);//像素尺寸
int height = Height/2-3*qAbs(mouse_move_length);//数字框宽度
int y = Height/2+mouse_move_length-height/2;//数字框x座标
QFont font;
font.setPixelSize(size);
painter->setFont(font);
if(mid == 1)
painter->setPen(currentNum_color);
else
painter->setPen(besideNum_color);
painter->drawText(QRectF(0,y,Width,height),Qt::AlignCenter,QString::number(num));
}
painter->restore();
}
void SlidingSelector::paint_parting_line(QPainter *painter)
{
painter->save();
QPen pen;
pen.setBrush(partingLine_color);
pen.setWidth(4);
pen.setCapStyle(Qt::RoundCap);
pen.setStyle(Qt::SolidLine);
painter->setPen(pen);
if(mode == Horizontal)
{
int up_down_space=height()/10;
QPoint line_left_up=QPoint(space+(width()-space*2)/3,space+up_down_space);
QPoint line_left_down=QPoint(space+(width()-space*2)/3,height()-space-up_down_space);
QPoint line_right_up=QPoint(space+((width()-space*2)/3)*2,space+up_down_space);
QPoint line_right_down=QPoint(space+((width()-space*2)/3)*2,height()-space-up_down_space);
painter->drawLine(line_left_up,line_left_down);//绘制左侧分割线
painter->drawLine(line_right_up,line_right_down);//绘制右侧分割线
}
else
{
int up_down_space=width()/10;
QPoint line_left_up=QPoint(space+up_down_space,space+(height()-space*2)/3);
QPoint line_left_down=QPoint(space+(width()-space*2)-up_down_space,space+(height()-space*2)/3);
QPoint line_right_up=QPoint(space+up_down_space,space+((height()-space*2)/3)*2);
QPoint line_right_down=QPoint(space+(width()-space*2)-up_down_space,space+((height()-space*2)/3)*2);
painter->drawLine(line_left_up,line_left_down);//绘制左侧分割线
painter->drawLine(line_right_up,line_right_down);//绘制右侧分割线
}
painter->restore();
}