前言
QML 中没有直接提供类似 android 的ExpandableListView二级列表控件,treeView,但是用 treeView 实在是有些不方便,并且达不到想要的效果,所以干脆用 ListView 来扩展一个,这其中也参考了网上一些用法,大致思路差不多,这里做一下总结。
效果图
每一级可随意展开或收起。
实现
实现该效果主要有如下几个关键点:
model 的实现
可以将整体理解为两个列表,第一个是画红线部分,是最外层的列表, 第二个是每个内层的列表,画蓝线部分。 并且两个列表对应的元素都不一样,但是 model 只有一个,那么就需要在一个 model 中插入一个子 model 来提供给内层列表使用。 我这里的 model 列表结构如下:listModel.append({"meetingName":meetingName,"date":date,"level":0,
"subNode":[{"fileName":fileName,"size":size,"level":1,"subNode":[]}]})
subNode就是子节点,提供给内层列表使用的 model,其实按照这种思路,可以衍生无数个子节点,效果类似于 treeView。
delegate 的实现
delegate的实现也是非常关键的一点,先看图
delegate其实就是划红线的整体,要想实现可收缩的效果,需要用到Column,当点击外层 Item 的时候,将内层列表的所有元素(也就是蓝线的部分)隐藏,隐藏后 Column 会自动收缩,而内层列表则是通过Repeater来重复创建的,而 Repeater 的 model 则是上面说的 subNode子节点。
OK,说这么多,不妨看看代码
源码
listview 创建
ListView{
id:listView
anchors.fill: parent
anchors.top: parent.top
anchors.topMargin:20
spacing: 20
Material.background: "white"
model: ListModel{
id:listModel
}
delegate: list_delegate
}
重点是接下来的 delegate
Component{
id:list_delegate
Column{
id:objColumn
Component.onCompleted: {
for(var i = 1; i < objColumn.children.length - 1; ++i) {
objColumn.children[i].visible = false
}
}
MouseArea{
width:listView.width
height: objItem.implicitHeight
enabled: objColumn.children.length > 2
onClicked: {
console.log("onClicked..")
var flag = false;
for(var i = 1; i < parent.children.length - 1; ++i) {
console.log("onClicked..i=",i)
flag = parent.children[i].visible;
parent.children[i].visible = !parent.children[i].visible
}
console.log("onClicked..flag = ",flag)
if(!flag){
iconAin.from = 0
iconAin.to = 90
iconAin.start()
}
else{
iconAin.from = 90
iconAin.to = 0
iconAin.start()
}
}
Row{
id:objItem
spacing: 10
leftPadding: 20
Image {
id: icon
width: 10
height: 20
source: "icon_retract.png"
anchors.verticalCenter: parent.verticalCenter
RotationAnimation{
id:iconAin
target: icon
duration: 100
}
}
Label{
id:meeting_name
text: meetingName
font.pixelSize: fontSizeMedium
anchors.verticalCenter: parent.verticalCenter
}
Label{
text: date
font.pixelSize: fontSizeMedium
color:"grey"
anchors.verticalCenter: parent.verticalCenter
}
}
}
Repeater {
model: subNode
delegate: Rectangle{
width: 500
height: 120
Rectangle {
id: fileicon
width: 80
height: 80
color:index%2?"red":"yellow"
anchors{
left: parent.left
leftMargin: 20
verticalCenter: parent.verticalCenter
}
}
Column{
anchors{
left: fileicon.right
leftMargin: 20
top: parent.top
topMargin:20
}
topPadding: 10
spacing: 10
Label{
text: model.fileName
font.pixelSize: fontSizeMedium
}
Label{
text: model.size
font.pixelSize: fontSizeMedium
color: "grey"
}
}
RoundButton{
id:download
width: 90
height: 40
highlighted: true
radius: height/2.
text: qsTr("View")
anchors{
right: parent.right
rightMargin: 30
verticalCenter: parent.verticalCenter
}
}
}
}
}
}
对于 model 的数据插入,我定义了一个函数
function addModelData(meetingName,date,fileName,size){
var index = findIndex(meetingName)
if(index === -1){
listModel.append({"meetingName":meetingName,"date":date,"level":0,
"subNode":[{"fileName":fileName,"size":size,"level":1,"subNode":[]}]})
}
else{
listModel.get(index).subNode.append({"fileName":fileName,"size":size,"level":1,"subNode":[]})
}
}
function findIndex(name){
for(var i = 0 ; i < listModel.count ; ++i){
if(listModel.get(i).meetingName === name){
return i
}
}
return -1
}
只需要调用addModelData传入相应的数据即可,findIndex函数是用来查找当前 model 中是否已经存在了外层数据元素,代码都很简单明了,就不一一描述了。
插入数据示例:
addModelData("class A","2018.2.1","aaa","13kb")
addModelData("class B","2018.2.4","ddd","43kb")
欢迎提出意见和建议。
参考文章:https://www.codeproject.com/Articles/632795/QML-TreeModel-and-TreeView