QML实现一个TreeView

0 前言

在Qt5中的QtQuick.Controls 2.x中没有TreeView这个控件,而只在QtQuick.Controls 1.x中拥有这个控件。因此当我们使用高版本的QtQuick.Controls时,无法使用TreeView,因此我们必须得想办法自己实现一个TreeView。

1 设计思路

实现TreeView的最容易想到的思路就是迭代使用ListView,ListView我们是可以使用的,而TreeView实际上就是ListView套ListView(套娃),因此灵活使用ListView就能制作一个TreeView。

1.1 TreeView的数据格式(model)

我这里采取的是读取JSON格式的数据用来作为TreeView的model(模型数据),下面是一个简单的模型数据例子:

[
  {
    "title": "第一章"
  },
  {
    "title": "第二章",
    "childrens": [
      {
        "title": "第二章-第一节"
      },
      {
        "title": "第二章-第二节"
      }
    ]
  },
  {
    "title": "第三章"
  }
]

上面就是TreeView需要读取的数据模型,只要我们修改这个数据模型,就可以构造出我们想要的任意的TreeView显示类型。

1.2 TreeView的显示样式(delegate)

delegate这部分是重点,如何设计这个delegate是整个TreeView能否正确显示的重中之重。

delegate的显示样式如上图所示,我们要使用一个Component来实现上面这个部分,下面是Component的简易代码(具体代码后面会给出):

Component {
  Item {
    RowLayout { // 主题(图标、标题)部分采用行布局
      Image { // 图标
        
      }
      Text { // 标题
        
      }
    }

    ColumnLayout { // 子项目中采用列布局
      Item {
        ListView { // 子项目中也应该有一个ListView,实现迭代
          
        }
      }
    }
  }
}

这里需要注意的一个细节是:Component的高度 = RowLayout(主题)的高度 + ColumnLayout(子项目)的高度,这样显示出来才不会重叠。

2 最终效果和代码

2.1 效果图

最终实现的效果如上图所示:下面就是一个TreeView,可以看到可以进行多级展开,并且图标变化部分使用了动画,而不是两种图片切换的方式,这样更有动态变化的效果。

2.2 附代码

import QtQuick 2.15
import QtQuick.Controls 2.15
import QtQuick.Layouts 1.3

Item {
    id: root
    width: 360
    height: 300
    // 使用别名model,并为listView提供数据模型
    property alias model: listView.model

    // 设置背景颜色
    Rectangle {
        id: bg
        anchors.fill: parent
        color: "#353535"
    }

    ListView {
        id: listView
        anchors.fill: parent
        delegate: listDelegate
    }

    // 定义数据显示方式
    Component {
        id: listDelegate
        Item {
            id: item
            width: parent.width
            height: itemTitle.height + itemChildren.height
            property bool isSpread: false // 当前节点是否展开
            function clickedEvent() { // 鼠标点击时需要完成的事件
                console.log(modelData.title, "on clicked")
                itemChildren.visible = !itemChildren.visible
                if (item.isSpread) {
                    iconAnimation.from = 90
                    iconAnimation.to = 0
                    iconAnimation.start()
                    item.isSpread = !item.isSpread
                } else {
                    iconAnimation.from = 0
                    iconAnimation.to = 90
                    iconAnimation.start()
                    item.isSpread = !item.isSpread
                }
            }

            // 主题(图标、标题)使用行布局
            RowLayout {
                id: itemTitle
                anchors.left: parent.left
                spacing: 5

                // 图标
                Image {
                    id: itemICON
                    source: "qrc:/CaretRight.png"
                    Layout.preferredWidth: 25
                    Layout.preferredHeight: 25
                    RotationAnimation {
                        id: iconAnimation
                        target: itemICON
                        duration: 100
                    }

                    MouseArea {
                        anchors.fill: parent
                        onClicked: {
                            item.clickedEvent()
                        }
                    }
                }

                // 标题
                Text {
                    text: qsTr(modelData.title)
                    font {
                        pixelSize: 18
                    }
                    color: "white"
                    Layout.fillWidth: true
                    MouseArea {
                        anchors.fill: parent
                        onClicked: {
                            item.clickedEvent()
                        }
                    }
                }
            }

            // 子项使用列布局
            ColumnLayout {
                id: itemChildren
                visible: modelData.hasChildren && item.isSpread // 有子项目并且展开显示
                anchors.left: parent.left
                anchors.top: itemTitle.bottom
                anchors.leftMargin: 25
                height: itemChildren.visible ? itemChildrenListView.contentHeight : 0
                spacing: 5
                Item {
                    width: parent.width - 20
                    height: itemChildrenListView.contentHeight
                    ListView {
                        id: itemChildrenListView
                        anchors.fill: parent
                        delegate: listDelegate
                        model: modelData.childrens
                    }
                }
            }
        }

    }
}
import QtQuick 2.15
import QtQuick.Controls 2.15

Item {
    id: item1
    width: 800
    height: 700

    // 用来设置背景颜色
    Rectangle {
        id: rect_bg
        anchors.fill: parent
        color: "#252525"
    }

    // 主题:帮助
    Text {
        id: text_help
        width: 70
        height: 30
        text: qsTr("帮助")
        color: "#ffffff"
        anchors.left: parent.left
        anchors.top: parent.top
        font.pixelSize: 18
        horizontalAlignment: Text.AlignLeft
        verticalAlignment: Text.AlignVCenter
        font.bold: true
        anchors.topMargin: 10
        anchors.leftMargin: 10
    }

    // 按钮行布局
    Row {
        id: row
        width: 500
        height: 40
        anchors.left: parent.left
        anchors.top: text_help.bottom
        spacing: 30
        anchors.topMargin: 10
        anchors.leftMargin: 10

        Button {
            id: btn_userMannual
            width: 150
            height: parent.height
            text: qsTr("在线用户手册")
            background: Rectangle {
                color: "#177ddc"
            }
        }

        Button {
            id: btn_communicateUs
            width: 150
            height: parent.height
            text: qsTr("联系我们")
            background: Rectangle {
                color: "#177ddc"
            }
        }
    }

    // 主题:常见问题
    Text {
        id: text_commonQs
        width: 70
        height: 30
        text: qsTr("常见问题")
        color: "#ffffff"
        anchors.left: parent.left
        anchors.top: row.bottom
        font.pixelSize: 18
        horizontalAlignment: Text.AlignLeft
        verticalAlignment: Text.AlignVCenter
        font.bold: true
        anchors.topMargin: 10
        anchors.leftMargin: 10
    }

    // 实现的TreeView
    MTreeView {
        id: treeView
        width: 780
        height: 500
        anchors.left: parent.left
        anchors.right: parent.right
        anchors.bottom: parent.bottom
        anchors.top: text_commonQs.bottom
        anchors.margins: 10
        Component.onCompleted: {
            setTreeViewModelData()
        }
    }

    // 该函数用于TreeView设置需要测试的模型数据(model)
    function setTreeViewModelData() {
        treeView.model = JSON.parse('[
            {
                "title": "第一章",
                "hasChildren": true
            },
            {
                "title": "第二章",
                "hasChildren": true,
                "childrens": [
                    {
                        "title": "第二章-第一节",
                        "hasChildren": false
                    },
                    {
                        "title": "第二章-第二节",
                        "hasChildren": true,
                        "childrens": [
                            {
                                "title": "第二章-第二节-第一段",
                                "hasChildren": false
                            },
                            {
                                "title": "第二章-第二节-第二段",
                                "hasChildren": false
                            }
                        ]
                    }
                ]
            },
            {
                "title": "第三章",
                "hasChildren": true
            }
        ]')
    }
}
import QtQuick 2.15
import QtQuick.Window 2.15
import QtQuick.Controls 2.15

Window {
    width: 640
    height: 480
    visible: true
    MPage {
        anchors.fill: parent
    }
}

3 总结

上述这种实现TreeView的方式仅供参考,目前仍然存在一些Bug,例如旁边的滚动条没有实现,并且在往上拉的时候会超过它的边界,如下图所示:

猜你喜欢

转载自blog.csdn.net/hu853712064/article/details/130890815