1. 程式人生 > 其它 >QT-可拖拽可編輯的多控制元件ListView

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

本文來自部落格園,作者:飛雲~,轉載請註明原文連結:https://www.cnblogs.com/cnscud/p/15020400.html