1. 简述
QML 中ListView没有自带这个功能,但是提供了自定义页头和页脚组件,控制组件有无,实现下拉刷新和上拉加载。刷新委托和加载委托支持用户完全自定义。
2. 代码
2.1 PullListViewV2.qml
import QtQuick
ListView {
id: listView
property bool headerVisible: false
property bool footerVisible: false
property bool headerHold: false
property bool footerHold: false
enum MoveDirection{
NoMove,
UpToDown,
DownToUp
}
property int moveDirection: PullListViewV2.NoMove
property real moveStartContentY: 0
onHeaderVisibleChanged: if(!headerVisible) {headerHold = false}
onFooterVisibleChanged: if(!footerVisible) {footerHold = false}
onContentYChanged: {
if(dragging || flicking)
{
moveDirection = (contentY - moveStartContentY < 0) ? PullListViewV2.UpToDown : PullListViewV2.DownToUp
// console.log("onContentYChanged:",atYBeginning,moveDirection,headerVisible)
switch(moveDirection){
case PullListViewV2.UpToDown:{
if(atYBeginning && !headerVisible && !footerVisible) {
headerVisible = true
}
}break;
case PullListViewV2.DownToUp:{
if(atYEnd && !headerVisible && !footerVisible) {
footerVisible = true
}
}break;
default:break;
}
}
}
//鼠标或手指拖动驱动的界面滚动
onDraggingChanged: dragging ? pullStart() : pullEnd()
//鼠标滚动驱动的view滚动
onFlickingChanged: flicking ? pullStart() : pullEnd()
function pullStart(){
console.log("pullStart")
moveStartContentY = contentY
}
function pullEnd(){
// console.log("pullEnd:",atYBeginning,moveDirection,headerVisible,contentY - moveStartContentY)
switch(moveDirection){
case PullListViewV2.UpToDown:{
if(atYBeginning && headerVisible) {
headerHold = true
}else if(null !== headerItem){
headerVisible = false
headerHold = false
}
}break;
case PullListViewV2.DownToUp:{
if(atYEnd && footerVisible) {
footerHold = true
}else if(null !== footerItem){
footerVisible = false
footerHold = false
}
}break;
default:break;
}
moveDirection = PullListViewV2.NoMove
}
}
2.2 main.qml
import QtQuick
Window {
width: 640
height: 480
visible: true
title: qsTr("Hello World")
Item{
anchors.fill: parent
PullListViewV2{
id: listView
anchors.fill: parent
model: listModel
delegate: Item {
id: name
height: 40
width: listView.width
Rectangle{
anchors.fill: parent
anchors.margins: 4
color: "red"
radius: 4
Text{
anchors.verticalCenter: parent.verticalCenter
anchors.leftMargin: 6
anchors.left: parent.left
text: "行:" + index
}
}
}
Component{
id: cmpHeader
Rectangle{
color: "yellow"
width: listView.width
height: 16
Text{
anchors.centerIn: parent
text: listView.headerHold ? "正在刷新" : "刷新"
}
}
}
header: headerVisible ? cmpHeader : null
onHeaderHoldChanged:{
if(headerHold)
timerRefresh.start()
}
Component{
id: cmpFooter
Rectangle{
color: "yellow"
width: listView.width
height: 16
Text{
anchors.centerIn: parent
text: listView.footerHold ? "正在加载..." : "加载更多"
}
}
}
footer: footerVisible ? cmpFooter : null
onFooterHoldChanged: {
if(footerHold)
timerLoadMore.start()
}
}
//刷新
Timer{
id: timerRefresh
interval: 1000
onTriggered: {
console.log("刷新完成")
listModel.refresh()
listView.headerVisible = false
}
}
//加载
Timer{
id: timerLoadMore
interval: 1000
onTriggered: {
console.log("加载完成")
listModel.loadMore()
listView.footerVisible = false
}
}
ListModel{
id: listModel
function refresh(){
listModel.clear()
for(var i = 0; i < 10; i ++){
listModel.append({name:listModel.count + 1})
}
}
function loadMore(){
for(var i = 0; i < 5; i ++){
listModel.append({name:listModel.count + 1})
}
}
function canLoadMore(){
return count > 30 ? false : true;
}
Component.onCompleted: refresh()
}
}
}