QT自定義控件--滑動選擇器

一、前言

這是基於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();

}
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章