QT-可拖拽可編輯的多控制元件ListView
阿新 • • 發佈:2021-07-16
目標
結合前面的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