QT-可拖拽可編輯的多控件ListView


目標

結合前面的2篇文章, 繼續升級QML版本的ListView: 又要拖拽, 又要可編輯, 還得支持多個控件.

循序漸進

本文基於前一篇的基礎: Qt-可編輯的ListView 要循序漸進的學習.

幾個關鍵點

  • 要用拖拽, 就不能用Layout了 (大部分情況應該是)
  • 條條大路通羅馬, 但是沒有找到官方的示例...只好自己寫
  • 盡量簡潔, 而不是自己控制所有狀態(雖然也可以做到, 就是太麻煩)

示意圖

代碼如下

main.cpp, main.qml沒啥變化, 和之前的一樣.

主要的EditDragList.qml代碼如下, 里面注釋很多, 還有很多調試打印, 自己可以去除:

import QtQuick 2.12
import QtQuick.Controls 2.5
import QtQuick.Layouts 1.3
import QtQml.Models 2.1


/**
  1. 通過設置currentIndex, 屬性自動變化. 支持鍵盤
  2. 支持拖拽
*/
Rectangle {
    id: root

    //列表容器
    Rectangle {
        id: itemRoot
        width: root.width
        height: root.height - itemOp.height

        //調試顯示
        //color: "blue"
        //border.width: 2

        Component {
            id: delegateItem

            MouseArea {
                id: mouseArea
                width: itemRoot.width

                height: itemRect.height
                anchors {
                    left: parent.left
                    right: parent.right
                }

                //hoverEnabled: true

                //拖拽設置
                drag.smoothed: false
                drag.target: itemRect
                drag.axis: Drag.YAxis



                onClicked: {
                    console.debug("onClicked")

                    //方法1: 設置當前
                    listView.currentIndex = index

                    console.log(("MouseArea Click listview currentIndex: "
                                 + listView.currentIndex + " index: " + index))
                    console.log(("MouseArea Click ListView isCurrentItem: "
                                 + ListView.isCurrentItem))

                    // 在onFocusChanged 中設置了, 此處不需要了
                    //textinput.focus = true;
                }


                onFocusChanged: {
                    if (focus) {
                        console.debug("=====got focus of mouseArea, start")
                        console.debug(("got focus of mouseArea, listview currentIndex: "
                                     + listView.currentIndex + " index: " + index))
                        console.debug("got focus of mouseArea, isCurrentItem: " +  mouseArea.ListView.isCurrentItem)
                        console.debug("got focus of mouseArea, drag is active: " + drag.active)
                        console.debug("got focus of mouseArea, textInput visible: " + textinput.visible)

                        textinput.focus = true

                        console.debug("=====got focus of mouseArea, end!")
                    }
                    else {
                        console.debug(("lost focus of mouseArea, listview currentIndex: "
                                     + listView.currentIndex + " index: " + index))

                        console.debug("lost focus of mouseArea, isCurrentItem: " +  mouseArea.ListView.isCurrentItem)
                    }
                }

                //FIXME: 目前某行處於編輯狀態, 然后其他行拖動和此行交換, 則會crash, 原因待查 2020.4.21
                //目前解決的思路: 一旦開始拖拽, 則退出編輯狀態
                drag.onActiveChanged: {
                    if (mouseArea.drag.active) {
                        //開始拖動時: 設置當前Item為空
                        listView.currentIndex = -1
                    }
                }

                //實際顯示內容
                Rectangle {
                    id: itemRect
                    height: 40
                    width: mouseArea.width

                    anchors {
                        horizontalCenter: parent.horizontalCenter
                        verticalCenter: parent.verticalCenter
                    }

                    //拖拽設置
                    Drag.active: mouseArea.drag.active
                    Drag.source: mouseArea
                    Drag.hotSpot.x: width / 2
                    Drag.hotSpot.y: height / 2


                    //拖拽的狀態變化
                    states: State {
                        when: itemRect.Drag.active

                        ParentChange {
                            target: itemRect
                            parent: itemRoot
                        }
                        AnchorChanges {
                            target: itemRect
                            anchors {
                                horizontalCenter: undefined
                                verticalCenter: undefined
                            }
                        }
                    }


                    CheckBox {
                        id: chkbox
                        width: 50
                        //height: parent.height
                        anchors.bottom: parent.bottom
                        //底部留點空間
                        bottomPadding: 3

                        checked: model.done
                        onClicked: model.done = checked
                    }

                    Rectangle {
                        id: textSection
                        height: parent.height

                        width: parent.width - chkbox.width
                        anchors.left: chkbox.right

                        Text {
                            id: textShow

                            text: model.description
                            anchors.bottom: parent.bottom

                            //控制可見: 不是當前
                            visible: !mouseArea.ListView.isCurrentItem

                            //底部留點空間
                            bottomPadding: 3

                        } //end textShow

                        TextInput {
                            id: textinput

                            anchors.bottom: parent.bottom

                            color: "blue"

                            //底部留點空間
                            bottomPadding: 3

                            text: model.description

                            //控制是否編輯狀態
                            visible: mouseArea.ListView.isCurrentItem
                            enabled: mouseArea.ListView.isCurrentItem

                            //focus: true
                            selectByMouse: true

                            //Debug: 不變不會進入的, 例如已經focus, 再次設置不會觸發此事件
                            onFocusChanged: {
                                if (focus) {
                                    console.debug("got focus " + "textInput")
                                }
                                else {
                                    console.debug("lost focus " + "textInput")
                                }
                            }

                            onEditingFinished: {
                                console.debug("=== start onEditingFinished ")
                                model.description = textinput.text

                                //方法1: 設置index
                                if (listView.currentIndex == index) {
                                    listView.currentIndex = -1
                                }

                                console.log(("TextInput listview currentIndex: "
                                             + listView.currentIndex + " index: " + index))
                                console.log(("TextInput ListView isCurrentItem: "
                                             + mouseArea.ListView.isCurrentItem))

                                console.debug("=== end onEditingFinished ")
                            }
                        } //end TextInput

                    } //end textSection Rectangle

                    Frame {
                        width: itemRect.width
                        height: 1
                        anchors.bottom: itemRect.bottom
                        //anchors.topMargin: 1

                    }

                } //end itemRect Rectangle


                DropArea {
                    anchors {
                        fill: parent
                        margins: 10
                    }


                    onEntered: {
                        console.debug("===== start DropArea onEntered")
                        console.debug("drag.source.DelegateModel.itemsIndex: " + drag.source.DelegateModel.itemsIndex)
                        console.debug("mouseArea.DelegateModel.itemsIndex: " + mouseArea.DelegateModel.itemsIndex )

                        //移動Delegate
                        visualModel.items.move(
                                    drag.source.DelegateModel.itemsIndex,
                                    mouseArea.DelegateModel.itemsIndex, 1)

                        //移動Model: 不移動的話model和delegate就不同步了
                        visualModel.model.move(
                                    drag.source.DelegateModel.itemsIndex,
                                    mouseArea.DelegateModel.itemsIndex, 1)

                        console.debug("===== end DropArea onEntered")
                    }
                }
                //end DropArea


            } //end MouseArea

        } //end Component

        DelegateModel {
            id: visualModel

            model: MyModel {}
            delegate: delegateItem
        }

        ListView {
            id: listView

            width: parent.width
            height: parent.height

            keyNavigationEnabled: true

            //clip: true
            model: visualModel
            //delegate: delegateItem

            //默認不要是第一個, 否則第一個是可編輯狀態(針對方法1)
            Component.onCompleted: {
                currentIndex = -1
            }

            //默認焦點
            focus: true

            spacing: 0
        }
    }

    //操作區容器
    Rectangle {
        id: itemOp

        width: root.width
        //指定高度
        height: 50

        //和上一個區域挨着
        anchors.top: itemRoot.bottom

        Button {
            text: qsTr("Add New Item")
            width: parent.width

            onClicked: {
                var c = listView.model.model.rowCount()
                //插入在第一個
                listView.model.model.insert(0, {
                                                "description": "Buy a new book " + (c + 1),
                                                "done": false
                                            })

                //追加
                //listView.model.model.append({ "description": "Buy a new book " + (c + 1), "done": false })

                //設置焦點, 否則listView就沒焦點了
                listView.focus = true
                listView.currentIndex = 0
            }
        }
    }
}

項目地址

和前一篇一樣, 還在同一個項目里面: https://github.com/cnscud/learn/tree/master/qt/editListView


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM