这篇文章将要介绍Qml中如何实现拖拽功能。实现拖拽并释放控件需要下面几个步骤:
为了处理鼠标事件,我们可以在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的值,所以矩形框跳动得厉害。如何解决这个问题呢?
使用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动态添加控件。