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:如何通过拖放重新排序中继器项目?里面有一些工作代码的主要内容,如果未能解决你的问题,请参考以下文章

Android GridView 通过拖放重新排序元素

PyQt:QListView 拖放重新排序信号问题

在 PyQt 中使用拖放重新排序 QTreeWidget 中的项目

QTreeView - 如何判断拖放事件是重新排序还是父母之间的移动?

在 Flutter 中通过拖放重新排序 SliverList 中的项目

如何正确使用 QQuickItem::stackBefore() 重新排序 GridLayout 中的项目?