QML:如何通过拖放重新排序中继器项目?里面有一些工作代码
Posted
技术标签:
【中文标题】QML:如何通过拖放重新排序中继器项目?里面有一些工作代码【英文标题】:QML: How to re-order repeater items with drag and drop? Somewhat working code inside 【发布时间】:2019-01-18 10:49:05 【问题描述】:我正在尝试使用拖放重新排序中继器项目。由于动态加载的数据,我正在使用中继器。以下是我到目前为止使用添加了示例数据的简单 sqlite 数据库的内容。我希望设置重新排序,这样当用户释放拖动的对象时,数据库会更新以反映新的顺序。 “更改”按钮会根据需要重新排序元素,我只是无法通过拖放使其工作属性。
loadData() 函数只是创建表并插入示例数据。 “更改”按钮不会出现在最终代码中,我只是想用它来让重新排序的代码工作。
import QtQuick.Controls 2.2 as QC2
import QtQuick 2.10
import QtQuick.Window 2.10
import QtQuick.LocalStorage 2.0
Window
id: root
width: 320
height: 480
property var db
property int rowHeight: 90
Component.onCompleted:
db = LocalStorage.openDatabaseSync("test_db", "1.0", "Database", 100000)
loadData()
property var sql_data: []
function loadData()
var sql, rs, len, i
db.transaction(function(tx)
tx.executeSql("DROP TABLE IF EXISTS tbl")
tx.executeSql("CREATE TABLE IF NOT EXISTS tbl (
rowId INTEGER NOT NULL PRIMARY KEY AUTOINCREMENT,
name TEXT NOT NULL, listOrder INTEGER NOT NULL)")
len = 5
for (i = 0; i < len; i++)
tx.executeSql("INSERT INTO tbl (name, listOrder) VALUES ('Item " + i + "', " + i + ")")
rs = tx.executeSql("SELECT name, listOrder FROM tbl ORDER BY listOrder ASC")
)
len = rs.rows.length
sql_data = new Array(len)
for (i = 0; i < len; i++)
sql_data[i] = "name": "", "order": ""
sql_data[i]["name"] = rs.rows.item(i).name
sql_data[i]["order"] = rs.rows.item(i).listOrder
repeater.model = sql_data
Flickable
id: mainFlick
anchors.fill: parent
contentHeight: mainColumn.height
Column
id: mainColumn
width: parent.width
QC2.Button
text: "Change"
onClicked:
var p1, p2
var d1 = new Date().getTime()
var movedEle = 3
var newPos = 1
var ele1 = repeater.itemAt(movedEle)
var ele2 = repeater.itemAt(newPos)
ele1.y = ele2.y
root.sql_data[movedEle]["order"] = root.sql_data[newPos]["order"]
if (newPos < movedEle)
p1 = newPos
p2 = movedEle
else
p1 = movedEle
p2 = newPos
root.db.transaction(function(tx)
var sql = "UPDATE tbl SET listOrder = " + root.sql_data[p1]["order"] + " "
sql += "WHERE listOrder = " + (root.sql_data[p2]["order"])
for (var i = p1; i < p2; i++)
if (i !== movedEle)
repeater.itemAt(i).y = repeater.itemAt(i).y + rowHeight
root.sql_data[i]["order"] += 1
sql = "UPDATE tbl SET listOrder = " + root.sql_data[i]["order"] + " "
sql += "WHERE listOrder = " + (root.sql_data[i]["order"] - 1)
tx.executeSql(sql)
)
sql_data = sql_data.sort(function(a,b)
return a["order"] - b["order"]
)
repeater.model = sql_data
var d2 = new Date().getTime()
console.debug("Seconds: " + (d2 - d1) / 1000)
Repeater
id: repeater
width: parent.width
model: []
delegate: Column
id: repeaterItem
width: parent.width - 20
height: rowHeight
anchors.horizontalCenter: parent.horizontalCenter
property int pos: index
DropArea
id: dragTarget
width: parent.width
height: 20
Rectangle
id: dropRect
anchors.fill: parent
color: "green"
states: [
State
when: dragTarget.containsDrag
PropertyChanges
target: dropRect
color: "red"
]
Row
id: contentRow
width: parent.width
height: parent.height
z: 1
Column
id: itemColumn
width: parent.width - 70
Text
text: "Name: " + modelData["name"]
Text
text: "Order: " + modelData["order"]
Text
text: "Repeater Index: " + index
// itemColumn
MouseArea
id: dragArea
width: 40
height: itemColumn.height
drag.axis: Drag.YAxis
drag.target: dragRect
onReleased: parent = ((dragRect.Drag.target !== null) ? dragRect.Drag.target : root)
Rectangle
Component.onCompleted: console.debug(dragRect.Drag.target)
id: dragRect
width: 40
height: itemColumn.height
color: "grey"
Drag.active: dragArea.drag.active
Drag.source: dragArea
Drag.hotSpot.x: width / 2
Drag.hotSpot.y: height / 2
states : State
when: dragArea.drag.active
ParentChange target: dragRect; parent: root
AnchorChanges
target: dragRect
anchors.verticalCenter: undefined
anchors.horizontalCenter: undefined
// contentRow
Behavior on y
PropertyAnimation
duration: 200
easing.type: Easing.Linear
// repeaterItem
// repeater
// mainColumn
// mainFlick
// id
大部分拖放代码来自Qt Examples site。
这是我的问题:
-
用于拖动矩形的 MouseArea 似乎没有改变位置。一旦实际移动了一个矩形,它就会停留在新位置,但如果我想再次移动它,我必须在应用加载时单击并拖动它原来的位置。
如果我将 Drag Area 的目标从子矩形切换到整行(repeaterItem),一切都会正常移动,但不会再与 Drop 区域交互。
我想我可以通过简单地通过获取放置区域的 y 值来获取拖动行后新位置的索引。有没有更好的方法来做到这一点?
由于“更改”按钮已经对行元素重新排序,我只需要帮助让行与放置区域正确交互,然后在拖动项目时获取放置区域的 y 位置发布。
【问题讨论】:
我认为您可以通过将 MouseArea 锚定到 Rectangle 来解决您的第一个问题 @Amfasis 但是 Rectangle 是 MouseArea 的子元素,并且元素不能锚定到它的子元素。 嗯,误解了并且没有完全阅读代码,但我想说 mouseArea 应该以某种方式锚定到整个代表(如果需要,部分填充它) @Amfasis 感谢您的回复。不幸的是,当我将 MouseArea 锚定到 Rectangle 或整行本身时,一切都被冻结在原地,我无法再拖动任何东西。 【参考方案1】:这可能不是最有效的,但对于任何想要做类似事情的人来说,这是一个好的开始。
import QtQuick.Controls 2.2 as QC2
import QtQuick 2.10
import QtQuick.Window 2.10
import QtQuick.LocalStorage 2.0
Window
id: root
width: 320
height: 580
property var db
property int rowHeight: 90
property int len
Component.onCompleted:
db = LocalStorage.openDatabaseSync("test_db", "1.0", "Database", 100000)
loadData()
property var sql_data: []
function loadData()
var sql, rs, i
db.transaction(function(tx)
tx.executeSql("DROP TABLE IF EXISTS tbl")
tx.executeSql("CREATE TABLE IF NOT EXISTS tbl (
rowId INTEGER NOT NULL PRIMARY KEY AUTOINCREMENT,
name TEXT NOT NULL, listOrder INTEGER NOT NULL)")
len = 15
for (i = 0; i < len; i++)
tx.executeSql("INSERT INTO tbl (name, listOrder) VALUES ('Item " + i + "', " + i + ")")
rs = tx.executeSql("SELECT name, listOrder FROM tbl ORDER BY listOrder ASC")
)
len = rs.rows.length
sql_data = new Array(len)
for (i = 0; i < len; i++)
sql_data[i] = "name": "", "order": ""
sql_data[i]["name"] = rs.rows.item(i).name
sql_data[i]["order"] = rs.rows.item(i).listOrder
repeater.model = sql_data
Flickable
id: mainFlick
anchors.fill: parent
contentHeight: mainColumn.height
rebound: Transition
NumberAnimation
properties: "y"
duration: 500
easing.type: Easing.OutCirc
Column
id: mainColumn
width: parent.width
function moveEle(start, end, f)
if (f === false)
var p1, p2, ele1, ele2
var c = false
var d1 = new Date().getTime()
ele1 = repeater.itemAt(start)
console.debug(f)
ele2 = repeater.itemAt(end)
root.sql_data[start]["order"] = root.sql_data[end]["order"]
if (end < start)
p1 = end
p2 = start
else
p1 = start
p2 = end
c = true
root.db.transaction(function(tx)
var sql = "UPDATE tbl SET listOrder = " + root.sql_data[p1]["order"] + " "
sql += "WHERE listOrder = " + (root.sql_data[p2]["order"])
for (var i = p1; i < p2; i++)
if (c === false)
if (i !== start)
root.sql_data[i]["order"]++
sql = "UPDATE tbl SET listOrder = " + root.sql_data[i]["order"] + " "
sql += "WHERE listOrder = " + (root.sql_data[i]["order"] - 1)
tx.executeSql(sql)
else
root.sql_data[i]["order"]--
sql = "UPDATE tbl SET listOrder = " + root.sql_data[i]["order"] + " "
sql += "WHERE listOrder = " + (root.sql_data[i]["order"] - 1)
tx.executeSql(sql)
)
else if (f === true)
c = false
d1 = new Date().getTime()
ele1 = repeater.itemAt(start)
console.debug(f)
end--
ele2 = repeater.itemAt(end)
root.sql_data[start]["order"] = root.sql_data[end]["order"]
p1 = start
p2 = end
c = true
root.db.transaction(function(tx)
var sql = "UPDATE tbl SET listOrder = " + root.sql_data[p1]["order"] + " "
sql += "WHERE listOrder = " + (root.sql_data[p2]["order"])
tx.executeSql(sql)
for (var i = p1; i <= p2; i++)
if (i !== start)
root.sql_data[i]["order"]--
sql = "UPDATE tbl SET listOrder = " + root.sql_data[i]["order"] + " "
sql += "WHERE listOrder = " + (root.sql_data[i]["order"] - 1)
tx.executeSql(sql)
)
sql_data = sql_data.sort(function(a,b)
return a["order"] - b["order"]
)
repeater.model = sql_data
var d2 = new Date().getTime()
console.debug("Seconds: " + (d2 - d1) / 1000)
Repeater
id: repeater
width: parent.width
model: []
delegate: Column
id: repeaterItem
width: parent.width
height: rowHeight
anchors.horizontalCenter: parent.horizontalCenter
z: dragArea.drag.active ? 2 : 1
property int pos: index
DropArea
id: dragTarget
width: parent.width
height: 15
property int ind: index
onEntered:
drag.source.ind = index
drag.source.f = false
if (drag.source.ind !== drag.source.oldInd && drag.source.ind !== drag.source.oldInd + 1)
drag.source.caught = true
onExited: drag.source.caught = false
Rectangle
id: dropRect
anchors.fill: parent
z: 0
states: [
State
when: dragTarget.containsDrag
PropertyChanges
target: dropRect
color: "grey"
PropertyChanges
target: dropRectLine
visible: false
]
Rectangle
id: dropRectLine
width: parent.width
height: 1
anchors.verticalCenter: parent.verticalCenter
color: "black"
Row
id: contentRow
width: parent.width
height: parent.height
Drag.active: dragArea.drag.active
Drag.source: dragArea
Drag.hotSpot.x: width / 2
Drag.hotSpot.y: height / 2
Column
id: itemColumn
width: parent.width - 70
Text
text: "Name: " + modelData["name"]
Text
text: "Order: " + modelData["order"]
Text
text: "Repeater Index: " + index
// itemColumn
MouseArea
id: dragArea
width: 40 //repeater.width
height: itemColumn.height
drag.axis: Drag.YAxis
drag.target: contentRow
property point beginDrag
property point dropTarget
property bool caught: false
property int ind
property int oldInd: index
property bool f
onPressed:
dragArea.beginDrag = Qt.point(contentRow.x, contentRow.y);
onReleased:
if (dragArea.caught)
dropRectFinal.color = "white"
dropRectLineFinal.visible = true
mainColumn.moveEle(index,dragArea.ind, dragArea.f)
else
backAnimY.from = contentRow.y;
backAnimY.to = beginDrag.y;
backAnim.start()
Rectangle
id: dragRect
width: 40
height: itemColumn.height
color: "grey"
// contentRow
// dragArea
ParallelAnimation
id: backAnim
SpringAnimation id: backAnimY; target: contentRow; property: "y"; duration: 300; spring: 2; damping: 0.2
// repeaterItem
// repeater
DropArea
id: dragTargetFinal
width: parent.width
height: 15
property int ind: root.len
onEntered:
drag.source.ind = ind
drag.source.f = true
if (drag.source.ind !== drag.source.oldInd && drag.source.ind !== drag.source.oldInd + 1)
drag.source.caught = true
onExited: drag.source.caught = false
Rectangle
id: dropRectFinal
anchors.fill: parent
states: [
State
when: dragTargetFinal.containsDrag
PropertyChanges
target: dropRectFinal
color: "grey"
PropertyChanges
target: dropRectLineFinal
visible: false
]
Rectangle
id: dropRectLineFinal
width: parent.width
height: 1
anchors.verticalCenter: parent.verticalCenter
color: "black"
// mainColumn
QC2.ScrollBar.vertical: QC2.ScrollBar active: scrollAnim.running ? true : false
// mainFlick
DropArea
id: scrollUp
width: parent.width
height: 50
anchors.top: parent.top
visible:
var visible = false
if (mainFlick.contentY > 0)
visible = true
visible
onEntered:
scrollAnim.from = mainFlick.contentY
scrollAnim.to = 0
scrollAnim.start()
onExited: scrollAnim.stop()
DropArea
id: scrollDown
width: parent.width
height: 50
anchors.bottom: parent.bottom
visible:
var visible = false
if (mainFlick.contentY < mainColumn.height - Window.height)
visible = true
visible
onEntered:
scrollAnim.from = mainFlick.contentY
scrollAnim.to = mainColumn.height - Window.height
scrollAnim.start()
onExited: scrollAnim.stop()
SmoothedAnimation
id: scrollAnim
target: mainFlick
property: "contentY"
velocity: 400
loops: 1
maximumEasingTime: 10
// root
【讨论】:
以上是关于QML:如何通过拖放重新排序中继器项目?里面有一些工作代码的主要内容,如果未能解决你的问题,请参考以下文章
在 PyQt 中使用拖放重新排序 QTreeWidget 中的项目
QTreeView - 如何判断拖放事件是重新排序还是父母之间的移动?