今天,出于需求,需要在列表中用qt实现一个类似qml实现QQ列表界面侧划效果。经过我不懈打酱油+听音乐+发呆+刷新闻后终于得以实现,记录下~

对于这个神奇的控件,就称为DragBar,我所定义它的行为为:

DragBar

  1. 它有[打开]和[关闭]两种状态。
  2. 在[关闭]状态时,点击或者是拖动其划过超过某一个阀值距离后,进入[打开]状态。
  3. 在[打开]状态时,点击或者是拖动任意距离,都会进入[关闭]状态。
  4. 在[打开]状态时,会出现两个按钮,一个为[修改]一个为[删除]。
  5. 当用DragBar组成列表后,同一时间只有一个DragBar为[打开]状态。

因为是有拖动组件然后出现按钮这样的行为,所以我在实现时,利用MouseArea的drag属性,将两层组件覆盖在一起,于是如果你拖动上层组件,就自然而然会出现下层组件的外形。

drag provides a convenient way to make an item draggable.

  • drag.target specifies the id of the item to drag.
  • drag.active specifies if the target item is currently being dragged.
  • drag.axis specifies whether dragging can be done horizontally (Drag.XAxis), vertically (Drag.YAxis), or both (Drag.XAndYAxis)
  • drag.minimum and drag.maximum limit how far the target can be dragged along the corresponding axes.

这是Qt文档中对于drag属性的描述,所以可以很轻松的做如下设计。

DragBar结构

由于在对DragBar的定义上,又要响应点击又要响应拖拽,所以我把事件放在了MouseArea的released事件上,并通过判断触发该事件时的x坐标来区分这几种行为。

[关闭状态]

  • x<=阀值
    上层关闭
  • x>阀值
    上层打开
  • 如果是点击,即press和release时x坐标未改变
    上层打开

[打开状态]

  • 点击关闭

在定义第5条中'当用DragBar组成列表后,同一时间只有一个DragBar为[打开]状态',所以我将DragBar的打开关闭状态与activefocus属性关联,当activefocus为false时,即当焦点不在该控件上时,DragBar关闭。

好像差不多就这样,具体代码如下:

//DragBar.qml
import QtQuick 2.4
import QtQuick.Controls 1.2
import QtQuick.Controls.Private 1.0

Control {
    id:root

    implicitWidth: parent.width
    implicitHeight: 80

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

    property real boundary: 40 //滑动开关的边界值
    property alias isOpen: dragRec.isOpen //是否打开标志 true打开 false关闭

    onActiveFocusChanged: {
        if (!activeFocus) {
            isOpen = false
        }
    }

    Loader {
        id:backboard
        property Component __bkButtons //背景图标
        height: parent.height
        anchors.right: parent.right
        sourceComponent: __style && __style.buttons ? __style.buttons : __bkButtons
    }

    Loader {
        id: dragRec
        property Component __upPanel //上层面板
        property bool isOpen: false //是否打开标志 true打开 false关闭
        height: parent.height
        width: parent.width
        Drag.active: dragArea.drag.active
        sourceComponent: __style && __style.upper ? __style.upper : __upPanel

        onIsOpenChanged: {
            if (isOpen) {
                dropOpen.start()
            }else {
                dropClose.start()
            }
        }

        PropertyAnimation on x {
            id:dropOpen
            running:false
            to:dragArea.drag.minimumX
            easing.type: Easing.InOutQuad
        }

        PropertyAnimation on x {
            id:dropClose
            running:false
            to:dragArea.drag.maximumX
            easing.type: Easing.InOutQuad
        }

        MouseArea {
            id: dragArea
            anchors.fill: parent
            drag.target: parent
            drag.axis: Drag.XAxis
            drag.maximumX: 0
            drag.minimumX: -backboard.width
            property real oldx

            onPressed: {
                oldx = mouse.x
            }

            onReleased: {
                root.forceActiveFocus()
                console.log("dragRec.x:"+dragRec.x)
                if (!dragRec.isOpen) {
                    if (dragRec.x !== 0 && dragRec.x>-root.boundary) {
                        dropClose.start()
                    }else if (dragRec.x !== 0 || oldx===mouse.x){
                        dragRec.isOpen = true
                    }
                }else {
                    dragRec.isOpen = false
                }
            }
        }
    }

    function modifyfun(){
        console.log("修改操作")
    }

    function deletefun() {
        console.log("删除操作")
    }
}

这是对应的style样式的qml:

//DragBarStyle.qml
import QtQuick 2.4
import QtQuick.Controls 1.2
import QtQuick.Controls.Private 1.0

Style {
    id:style

    //背景上的按钮们
    property Component buttons: Row {
        height: parent.height

        Rectangle {
            height: parent.height
            width: 80
            color: "#f9cdac"

            Text{
                anchors.centerIn: parent
                text:"修改"
                color: "#ffffff"
            }

            MouseArea {
                anchors.fill: parent
                onClicked: {
                    control.modifyfun()
                }
            }
        }

        Rectangle {
            id:aaaa
            height: parent.height
            width: 80
            color: "#fc9d9a"

            Text{
                anchors.centerIn: parent
                text:"删除"
                color: "#ffffff"
            }

            MouseArea {
                anchors.fill: parent
                onClicked: {
                    control.deletefun()
                }
            }
        }
    }

    //上层面板样式
    property Component upper: Rectangle {
        anchors.fill: parent
        color: "#ffffff"
        Text {
            anchors.centerIn: parent
            text:"点一下或者拖拽就会打开"
            color: "#666666"
        }
    }

    //布局面板 可以设置底板背景颜色 不过一般看不见吧
    property Component panel: Item {
        anchors.fill: parent
    }
}

运行效果图:

~ 这个是全关闭的状态
关闭状态

~ 打开上面的DragBar
打开1

~ 打开下面的DragBar,上面的自动关闭
打开2