ListView的简单使用

二、ListView的简单使用
ListView 用来显示一个条目列表,条目对应的数据来自于Model,而每个条目的外观则由 Delegate 决定。我们可以将 Delegate 看成如何展示 Item 的一个模板。Android 手机上常见 的联系人界面,其实就是使用 ListView 实现的,而且 Android 的 ListView 和 Qt Quick 的 ListView 使用同样的模式:Model、View、Item Template (Delegate)。

我们先以 Qt Quick 内建 Model 为例,把使用 ListView 的方方面面都介绍一下,然后再看如何使用在 C++ 中实现自定义的 Model。

我构建了一个简单的手机列表,展示手机的型号、价格、制造商。使用上下键可以切换不同的手机,选中的手机有一个浅蓝色的高亮背景,同时字体放大,文字颜色变为红色。代码 phone_list_simple.qml:

i

mport QtQuick 2.2
import QtQuick.Controls 1.2
import QtQuick.Layouts 1.1

Rectangle {
    width: 360
    height: 300
    color: "#EEEEEE"

    // 1.定义delegate,内嵌三个Text对象来展示Model定义的ListElement的三个role
    Component {
        id: phoneDelegate
        Item {
            id: wrapper
            width: parent.width
            height: 30
            
            // 实现了鼠标点选高亮的效果
            MouseArea {
                anchors.fill: parent;
                onClicked: wrapper.ListView.view.currentIndex = index
            }
            
            // 内嵌三个Text对象,水平布局
            RowLayout {
                anchors.left: parent.left
                anchors.verticalCenter: parent.verticalCenter
                spacing: 8

                Text { 
                    id: col1;
                    text: name;
                    // 是否是当前条目
                    color: wrapper.ListView.isCurrentItem ? "red" : "black"
                    font.pixelSize: wrapper.ListView.isCurrentItem ? 22 : 18
                    Layout.preferredWidth: 120
                }
                
                Text { 
                    text: cost; 
                    color: wrapper.ListView.isCurrentItem ? "red" : "black"
                    font.pixelSize: wrapper.ListView.isCurrentItem ? 22 : 18
                    Layout.preferredWidth: 80
                }
                
                Text { 
                    text: manufacturer; 
                    color: wrapper.ListView.isCurrentItem ? "red" : "black"
                    font.pixelSize: wrapper.ListView.isCurrentItem ? 22 : 18
                    Layout.fillWidth: true
                }
            }
        }
    } // phoneDelegate-END
    
    // 2.定义ListView
    ListView {
        id: listView
        anchors.fill: parent

        // 使用先前设置的delegate
        delegate: phoneDelegate
        
        // 3.ListModel专门定义列表数据的,它内部维护一个 ListElement 的列表。
        model: ListModel {
            id: phoneModel

            // 一个 ListElement 对象就代表一条数据
            ListElement{
                name: "iPhone 3GS"
                cost: "1000"
                manufacturer: "Apple"
            }
            ListElement{
                name: "iPhone 4"
                cost: "1800"
                manufacturer: "Apple"
            }            
            ListElement{
                name: "iPhone 4S"
                cost: "2300"
                manufacturer: "Apple"
            } 
            ListElement{
                name: "iPhone 5"
                cost: "4900"
                manufacturer: "Apple"
            }    
            ListElement{
                name: "B199"
                cost: "1590"
                manufacturer: "HuaWei"
            }  
            ListElement{
                name: "MI 2S"
                cost: "1999"
                manufacturer: "XiaoMi"
            }         
            ListElement{
                name: "GALAXY S5"
                cost: "4699"
                manufacturer: "Samsung"
            }                                                  
        }

        // 背景高亮
        focus: true
        highlight: Rectangle{
            color: "lightblue"
        }
    }
}

执行 “qmlscene phone_list_simple.qml” 命令,可以看到如下图所示的效果。

为了示例简单,我直接在声明 ListView 对象时为 model 属性初始化了一个 ListModel。ListModel 是专门定义列表数据的,它内部维护一个 ListElement 的列表。一个 ListElement 对象就代表一条数据。

使用 ListElement 定义的数据条目可能是简单的,比如只有一个人名;也可能是复杂的,比如还有这个人的出生年月、性别;共同构成一个 ListElement 的一个或多个数据信息被称为 role,它包含一个名字(role-name)和一个值(role-value)。

role 的定义就像 QML 对象属性定义那样简单,语法是这样的:: ,其中 role-name 必须以小写字母开头,role-value 必须是简单的常量,如字符串、布尔值、数字或枚举值。

在 ListElement 中定义的 role,可以在 Delegate 中通过 role-name 来访问。示例定义的 ListElement 包含三个 role:name、cost、manufacturer,而 Delegate 则使用 Row 管理三个 Text 对象来展现这三个 role, Text 对象的 text 属性被绑定到 role-name 上。

ListView 的 delegate 属性类型是 Component,我在 phone_list_simple.qml 中定义了 id 为 phoneDelegate 的 Component。phoneDelegate 的顶层是 RowLayout,RowLayout内嵌三个 Text 对象来展示 Model 定义的 ListElement 的三个 role。

ListView 给 delegate 暴露了一个 index 属性,代表当前 delegate 实例对应的 Item 的索引位置,必要时可以使用它来访问数据。
示例中实现了鼠标点选高亮的效果:给 delegate 添加了一个 MouseArea 元素,在 onClicked 信号处理器中设置 ListView 的 currentlndex 属性。

ListView 定义了 delayRemove、isCurrentltem、nextSection、previousSection、section、view 等附加属性,以及 add、remove 两个附加信号,可以在 delegate 中直接访问。不过要注意的是,只有 delegate 的顶层 Item 才可以直接使用这些附加属性和信号,非顶层 Item 则需通过顶层Item的id来访问这些附加属性。

示例中的 delegate 组件,顶层 Item 是一个 Item 对象, 用于展示 name、cost、manufacturer 的 Text 对象通过 wrapper.ListView.isCurrentltem判断本 delegate 实例呈现的数据是否是当前条目,如果是,则改变文字大小和颜色。注意,我们是通过类名直接访问附加属性的。

示例中当前选中条目有一个浅蓝色的背景,它由 ListView 的 highlight 属性指定的 Component 提供,它的 Z 序小于 delegate 实例化出来的 Item 对象。示例通过给 highlight 初始 化一个 Rectangle 定义了高亮背景,如果你想实现复杂的高亮效果,也可以专门定义一个 Component。

与高亮效果相关的,还有很多属性,比如 highlightFollowsCurrentltem 属性指定高亮背景是否跟随当前条目,默认值为 true,你用鼠标点选某个 Item 时,高亮背景会经过一个平滑的动画后移动到新的 Item 下面。你可以设置它为 false 来禁用这种动画。

回到顶部
三、header
通过为 ListView 的 header 属性设置一个 Component,,用方向键浏览 Item 或者用鼠标在 ListView 内拖动时,表头随着拖动可能会变得不可见。

表头在某些应用场景下可以让数据的可读性更好。比如前面的手机信息示例,如果添加了表头,别人一看就知道每一列的数据含义。phone_list_header.qml 是修改后的文件,内容如下:

import QtQuick 2.2
import QtQuick.Controls 1.2
import QtQuick.Layouts 1.1

Rectangle {
    width: 360
    height: 300
    color: "#EEEEEE"
    
    // 1.定义header
    Component {
        id: headerView
        Item {
            width: parent.width
            height: 30
            RowLayout {
                anchors.left: parent.left
                anchors.verticalCenter: parent.verticalCenter
                spacing: 8

                Text { 
                    text: "Name"
                    font.bold: true
                    font.pixelSize: 20
                    Layout.preferredWidth: 120
                }
                // 省略。。。
            }            
        }
    }       
// 2.定义delegate
   Component {
        id: phoneDelegate
        Item {
            id: wrapper
            width: parent.width
            height: 30
            
            MouseArea {
                anchors.fill: parent
                onClicked: {
                    wrapper.ListView.view.currentIndex = index
                    console.log("index=", index)
                    }
            }      
            
            RowLayout {
                anchors.left: parent.left
                anchors.verticalCenter: parent.verticalCenter
                spacing: 8
                
                Text { 
                    id: col1;
                    text: name; 
                    color: wrapper.ListView.isCurrentItem ? "red" : "black"
                    font.pixelSize: wrapper.ListView.isCurrentItem ? 22 : 18
                    Layout.preferredWidth: 120
                }
                // 省略。。。
            }
        }
    }
    
// 3.定义model

Component {
id: phoneModel
ListModel {
ListElement{
name: “iPhone 3GS”
cost: “1000”
manufacturer: “Apple”
}
// 省略。。。
}
}

// 4.定义ListView
   ListView {
        id: listView
        anchors.fill: parent

        delegate: phoneDelegate
        model: phoneModel.createObject(listView)
        header: headerView
        focus: true
        highlight: Rectangle{
            color: "lightblue"
        }
    }
}

效果如下图所示。

headerView 是我定义的表头组件,与 delegate 组件定义类似,使用三个 Text 对象分别来描述每一列数据的含义,设定字体大小,让字体变粗,还设定了每一列的宽度。ListView 的 headerltem 属性保存了本 ListView 使用的、由 header 组件创建出来的 Item。

回到顶部
四、footer
footer 属性允许我们指定 ListView 的页脚,footerltem 保存了 footer 组件创建出来的 Item 对象,这个 Item会被添加到 ListView 的末尾,在所有可见的 Item 之后。

用 footer 可以干什么呢?随你吧。我这里的示例只是简单地在footer内放置了一个 Text对象,显示当前选中的Item的数据。有点儿像状态栏。

Rectangle {
    width: 360
    height: 300
    color: "#EEEEEE"
    
    // 省略header。。。
    
    // 2. 定义footer
    Component {
        id: footerView
        Text {
            width: parent.width
            font.italic: true
            color: "blue"
            height: 30
            verticalAlignment: Text.AlignVCenter
        }
    }
// 省略delegate和model。。。

// 5.定义ListView
  ListView {
        id: listView
        anchors.fill: parent

        delegate: phoneDelegate
        model: phoneModel.createObject(listView)
        header: headerView
        footer: footerView
        focus: true;
        highlight: Rectangle{
            color: "lightblue"
        }
        
        onCurrentIndexChanged:{
            if( listView.currentIndex >=0 ){
                var data = listView.model.get(listView.currentIndex);
                listView.footerItem.text = data.name + " , " + data.cost + " , " + data.manufacturer
            }
        }
    }        
}    

效果如下图所示。

为了使 footer 能够跟随当前 Item 发生变化,我为 listView 定义了 onCurrentlndexChanged 信号处理器,因为 currentlndexChanged 信号不带参数,所以只能再次访问 currentlndex 属性来获取当前 Item 的索引,然后通过 ListModel 的 get() 方法获取到对应的数据对象,最后呢, 我把 name、cost、manufacturer 三个 role 拼接在一块赋值给 footerltem。于是乎,当你点选一 个 Item 或者使用上下键浏览 Item 时,footer 就变化了。

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值