Qt中如何实现拖拽释放控件

这篇文章将要介绍Qml中如何实现拖拽功能。实现拖拽并释放控件需要下面几个步骤:

  1. 为控件添加鼠标响应事件,监听鼠标移动的横纵坐标。
  2. 根据鼠标的位置移动控件位置。
  3. 修改控件的宿主。

处理鼠标事件

为了处理鼠标事件,我们可以在qml文件中添加一个MouseArea对象。这个对象有位置和大小,但是不可见。这里为了演示MouseArea的使用,我们先添加一个click事件监听。

Rectangle{
        width: 100
        height: 100
        color: "green"

        MouseArea{
            anchors.fill:parent
            onClicked: {
                console.debug("onClick MouseArea")
            }
        }
    }

为了实现拖拽功能,我们需要监听更具体的一些事件。包括press事件、move事件、release事件。

MouseArea{
            anchors.fill:parent
            onClicked: {
                console.debug("onClick MouseArea")
            }
            onPressed: {
                console.debug("onPressed  x:",mouseX," y:",mouseY)
            }
            onPositionChanged: {
                console.debug("onPositionChanged x:",mouseX," y:",mouseY)
            }
            onReleased: {
                console.debug("onReleased  x:",mouseX," y:",mouseY)
            }
        }

onPressed监听鼠标按下事件,onPositionChanged监听鼠标位置变化,onRelease监听鼠标的释放事件。

移动控件

我们可以通过改变控件的x和y坐标来移动控件,这里先将mouseX和mouseY直接赋值给控件。

Rectangle{
        id:rect
        width: 100
        height: 100
        color: "green"

        MouseArea{
            anchors.fill:parent
            onClicked: {
                console.debug("onClick MouseArea")
            }
            onPressed: {
                console.debug("onPressed  x:",mouseX," y:",mouseY)
                rect.x = mouseX
                rect.y = mouseY
            }
            onPositionChanged: {
                console.debug("onPositionChanged x:",mouseX," y:",mouseY)
                rect.x = mouseX
                rect.y = mouseY
            }
            onReleased: {
                console.debug("onReleased  x:",mouseX," y:",mouseY)
                rect.x = mouseX
                rect.y = mouseY
            }
        }
    }

运行后进行拖拽实验,我们发现矩形框不断跳动,与我们期待的结构相差甚远。这里的MouseArea在矩形内部,在拖拽过程中不断修改矩形框的位置,矩形框的位置变换又影响mouseX和mouseY的值,所以矩形框跳动得厉害。如何解决这个问题呢?

  1. 不能直接使用mouseX和mouseY的值,因为这两个值依赖矩形位置。
  2. 通过x和y两个方向上的偏移量控制矩形移动。

使用Item的mapToGlobal 方法将mouseX和mouseY转换成globalX和globalY,避免修改矩形位置后影响MouseArea位置移动计算。通过记录前后两次的位置偏移量移动矩形。

Rectangle{
        id:rect
        width: 100
        height: 100
        x:300
        y:300
        color: "green"

        MouseArea{
            anchors.fill:parent
            property real lastX: 0
            property real lastY: 0
            onClicked: {
                console.debug("onClick MouseArea")
            }
            onPressed: {
                var coordinate = mapToGlobal(mouseX,mouseY)
                lastX = coordinate.x
                lastY = coordinate.y
            }
            onPositionChanged: {
                var coordinate = mapToGlobal(mouseX,mouseY)
                var offsetX = coordinate.x - lastX
                var offsetY = coordinate.y - lastY
                lastX = coordinate.x
                lastY = coordinate.y
                rect.x += offsetX
                rect.y += offsetY
            }
            onReleased: {
                var coordinate = mapToGlobal(mouseX,mouseY)
                var offsetX = coordinate.x - lastX
                var offsetY = coordinate.y - lastY
                lastX = coordinate.x
                lastY = coordinate.y
                rect.x += offsetX
                rect.y += offsetY
            }
        }
    }

经过上面的修改后,矩形拖拽的过程变得特别的顺畅了。

修改宿主

修改宿主指的是修改矩形框的parent,进而改变对象树结构和渲染树结构。

首先我们在拖拽释放的时候需要判断是否要修改矩形框的宿主,使用哪个宿主。这个处理过程需要根据鼠标释放时的坐标位置来判断。

function dockToHost(dragItem, globalX, globalY){
        var local = host1.mapFromGlobal(globalX,globalY)
        if(host1.contains(local)){
            dragItem.x = 0
            dragItem.y = 0
            dragItem.z = 100
            dragItem.parent = host1
            return
        }
        local = host2.mapFromGlobal(globalX, globalY)
        if(host2.contains(local)){
            dragItem.x = 0
            dragItem.y = 0
            dragItem.z = 100
            dragItem.parent = host2
            return
        }
    }

    Rectangle{
        id:host1
        width: 120
        height: 120
        x:100
        y:100
        color: "red"
    }
    Rectangle{
        id:host2
        width: 150
        height: 150
        x:220
        y:220
        color: "blue"
    }

鼠标释放的时候调用dockToHost方法用于确定矩形框改变到哪个宿主上。这里还调用了Item的mapFromGlobal方法将全局坐标转换成item本地坐标,然后判断是否在item区域内。找到宿主item后将矩形框的parent修改成宿主item并将矩形框的坐标重置。

总结

经过上面的步骤后我们可以实现一个简单的控件拖拽功能,并且通过拖拽控件可以改判控件的宿主item。这里主要应用到了MouseArea控件监听鼠标事件,通过改变矩形框的x和y偏移量修改位置。同时为了矩形框移动不影响到MouseArea的mouseX和mouseY,我们使用了mapToGlobal和mapFromGlobal方法来实现全局坐标与局部坐标的相互转换。然后我们通过修改矩形框的parent来改变矩形框的宿主,为宿主item动态添加控件。

你可能感兴趣的:(QT,qt,event)