嗯,其实在qt里有滚轮的控件,叫 ScrollBar ,在 Qt.labs.controls 1.0 包里,好吧,我就是闲的慌,想自己实现一个它的效果。=。=说起来汗颜的是,一开始看 Documentation 的时候,看到这个控件的几个属性,还各种的满不在乎,觉得尼玛这个设计好愚蠢。但当我实现了这个控件时,才发现,尼玛,我自己竟然也把这几个属性留出接口了,这真是,好尴尬啊……打脸啊……


分析下它的功能吧,仔细把玩了下竖向滚轮。

  • 滚轮的中间有一个滑块
  • 滑块占整体滚轮的比例跟文本区的可显示大小/整体文本大小有关
  • 滑块的显示高度跟当前可见文本区域在整个文本区域的位置有关
  • 滑块可以拖动的,拖动的时候会跟随鼠标的拖动方向移动
  • 滑块拖动的时候,它控制的文本也会同方向移动
  • 点击滑块上面的空白,页面会随着向上滚动,直至滑块到达鼠标位置
  • 点击滑块下面的空白,页面会随着向下滚动,直至滑块到达鼠标位置

其实,从描述上可以看到,如果就是简单的拿 Rectangle 堆出一个滚轮的话,它是一个三层结构。

三层结构大概的示意图

一开始我的第一反应是在中间的滑块上单独再有一个 MouseArea ,不过后来仔细琢磨了下发现其实那样设计会造成移动滑块的时候的处理变得更加复杂,其实那样设计除了看着舒心,并没有带来什么好处。所以,权衡之后,还是打算只用一个大 MouseArea 盖在上面,通过对鼠标坐标移动的信号来对不同的事件进行处理。

其实想通的话,整个滚轮实现起来并不难。不过有两个坑的地方,一个是按空白的地方,页面自动跑那里,我是靠了一个定时器来实现的页面滚动,通过定时页面高度自增自减。还一个地方其实就是滚轮移动比例到页面移动比例的换算千万别弄忘了,(。・`ω´・) 我一开始就忘记了,结果整个页面起飞了。


嗯,剩下这个控件很简单了,我依然将控件的逻辑部分和样式进行了分离,分别在 control 和 style 里,同时 control 里为了能够控制页面滚动,所以留了一个 Item 类型的属性,如果没对这个属性进行设置,会发现滚轮没有效果,但是不会报错的!

最终的大概效果。
控件的样子

代码如下:

control文件

//VerticalScrollBar.qml
import QtQuick 2.5
import QtQuick.Controls 1.3
import QtQuick.Controls.Private 1.0

Control {
    id: root
    implicitWidth: 10
    implicitHeight: parent.height

    style: Qt.createComponent("VerticalScrollBarStyle.qml", root)

    property Item handleItem // verticalscrollbar 控制的Flickable类控件,必须要设置哦,不设置滚轮是没有反应的
    property alias size: scrollbtn.height //滚轮的长度 height
    property alias position: scrollbtn.y //滚轮的高度 y

    property int __scrollState: 0 //-1向上滚 0不滚 1向下滚
    property int __scrollStep: 20 //按下空白时候的移动步长
    property double __offset: 0
    property bool __dragScroll: false //是否按下滚轮

    clip: true

    Timer {
        interval: 20
        running: !!handleItem
             &&__scrollState!==0
             && handleItem.contentY>=0
             && handleItem.contentY<=handleItem.contentHeight-handleItem.height
        repeat: true
        onTriggered: {
            if (!!handleItem) {
                handleItem.contentY += __scrollStep*__scrollState
            }
        }
    }

    Loader {
        id: backboard
        property Component __bkract //背景图案
        anchors.fill: parent
        sourceComponent: __style && __style.background ? __style.background : __bkract
    }

    Loader {
        id: scrollbtn
        property Component __scrollbtn //滑轮图案
        width: parent.width
        sourceComponent: __style && __style.scroll ? __style.scroll : __scrollbtn

        onYChanged: {
            if (y<mousearea.mouseY && y>mousearea.mouseY-height) {
                __scrollState = 0
            }
        }
    }

    MouseArea {
        id: mousearea
        anchors.fill: backboard

        property double oldY: -1
        onPressed: {
            if(mouseY<=scrollbtn.y) {//按住滚轮空白区域滚轮往上
                __scrollState = -1

            }else if(mouseY>=scrollbtn.y+scrollbtn.height) {//按住滚轮空白区域滚轮往下
                __scrollState = 1

            }else {
                __scrollState = 0
                __dragScroll = true
                oldY = mouseY

                __offset = !!handleItem? handleItem.contentY-oldY*handleItem.contentHeight/handleItem.height : 0
            }
        }
        onReleased: {
            __scrollState = 0
            __dragScroll = false
            oldY = -1
        }
        onPositionChanged: {
            if(!!handleItem && pressed && oldY!==-1) {
                var offsetMouseY = mouseY*handleItem.contentHeight/handleItem.height
                handleItem.contentY = __offset+offsetMouseY>=0? (__offset+offsetMouseY<=handleItem.contentHeight-handleItem.height? __offset+offsetMouseY : handleItem.contentHeight-handleItem.height) : 0
            }
        }
    }
}

style文件

//VerticalScrollBarStyle.qml
import QtQuick 2.5
import QtQuick.Controls 1.3
import QtQuick.Controls.Private 1.0

Style {
    id:style

    //背景样式
    property Component background: Rectangle {
        color: "#fcfcfc"
        Rectangle {
            width: 1
            height: control.height
            color: "#cccccc"
        }
    }

    //滚动条滚轮
    property Component scroll: Item {
        Rectangle {
            width: parent.width-4
            height: parent.height-4
            anchors.horizontalCenter: parent.horizontalCenter
            anchors.horizontalCenterOffset: 0.5
            anchors.verticalCenter: parent.verticalCenter
            color: control.__dragScroll? "#777777" : "#bbbbbb"
            radius: width
        }
    }
}

嗯,就是酱紫简单就哦了,具体使用例子,请烦劳移驾github吧~[(๑→ܫ←)github传送门]