QML-Model-View

一、Model-View

1.1、Qt中的model-view是什么

有点类似MVC模式,Qt中,数据通过model-view模式,将数据和显示分离开来

  • model: 数据模型,负责存储和管理数据
  • view: 视图容器,负责布局和显示管理
  • delegate: 可视化代理,负责单个数据项的可视化呈现

1.2、有哪些view

常见视图组件:

  • ListView - 列表视图
  • GridView - 网格视图
  • TableView - 表格视图
  • PathView - 路径视图
  • Repeater - 重复器(非视图组件,但常用于重复创建项目)

1.3、Repeater

Repeater是Qt中一个非常有用的组件,它可以重复渲染一组数据。它类似于其他框架中的列表或表格控件,但它的灵活性更高,因为它可以处理任意类型的模型(如字符串、图像等)。

  • 属性:
    • count: 创建多少项
    • delegate: 每个项的渲染模板
    • model: 数据源
  • 信号:
    • itemAdded(int index, Item item): 当一个新的项被添加到Repeater时发出。
      • onItemAdded(int index, Item item): 处理信号
    • itemRemoved(int index, Item item): 当一个项被从Repeater中移除时发出。
      • onItemRemoved: 处理信号

注意:

Repeater通常会创建其所有delegate项;如果存在大量delegate项,并且并非所有项都必须同时可见,会降低效率,需要考虑其他view

示例代码:

javascript 复制代码
import QtQuick 

Column{
    spacing:10
    Repeater{
        model: 5
        delegate: Rectangle{
            width: 100; height: 50
            color:"red"
        }
       onItemAdded: {
            console.log("item " + index + "added")
        }
        onItemRemoved: {
            console.log("item" + index + "removed")
        }
    }
}

1.4、ListView

类似于Repaeter,只不过delegate之间有间隔,并且可以滑动,所谓的瀑布流布局

关键属性:

  • spacing: 项间距
  • orientation: 布局方向(默认垂直)
  • clip: 是否裁剪超出区域的内容
  • cacheBuffer: 预渲染缓冲区大小
  • currentIndex: 当前选中项索引
  • highlight: 高亮组件

示例代码:

javascript 复制代码
ListView{
    anchors.fill: parent
    anchors.margins: 20
    spacing: 15
    clip: true                  // 默认是false,设置为true,在视觉上,会看不见元素创建与销毁的过程
    model: 100
    cacheBuffer: 20             // 如果需要展示的元素过多,可以设置缓存区大小,节省频繁创建销毁的内存开销

    delegate: Rectangle{
        required property int index
        width: 20
        height: 25
        color: "red"

        Component.onCompleted: {
            console.log(index + " added")
        }
        Component.onDestruction: {
            console.log(index + " deleted")
        }
    }
}

1.5、GridView

使用GridView可以创建网格布局,类似于ListView,只不过是将delegate布局成网格

关键属性:

  • cellWidthcellHeight: 网格大小
  • flow: 布局流向(FlowTopToBottom/FlowLeftToRight)
  • layoutDirection: 布局方向(LeftToRight/RightToLeft)

示例代码:

javascript 复制代码
GridView{
        anchors.fill: parent
        clip: true

        model:100

        cellWidth:40            // 设置每个小网格的长宽
        cellHeight:40

        flow:GridView.FlowTopToBottom   // 布局方式是从上到下
        //flow:GridView.FlowLeftToRight   // 布局方式是从左到右

        delegate: Rectangle{
            width: 20
            height: 20
            color: "red"
        }
}

1.6、Delegete

delegate翻译过来是代理,委托的意识;在model-view中,delegate是介于数据和视图之间的桥梁

  • 从model获取数据
  • 定义数据可视化方式
  • 响应视图状态变化

示例代码:

javascript 复制代码
    ListView{
        anchors.fill: parent
        anchors.margins: 20
        spacing: 15
        clip: true                  // 默认是false,设置为true,在视觉上,会看不见元素创建与销毁的过程
        model: 100
        cacheBuffer: 20             // 如果需要展示的元素过多,可以设置缓存区大小,节省频繁创建销毁的内存开销

        delegate: numberDelegate
    }
    Component{
        id: numberDelegate
        Rectangle{
            required property int index

            width: 40
            height: 25
            color: ListView.isCurrentItem ? "black" : "red"

            id: wrapper
            Text{
                id:label1
                text: wrapper.index

                anchors.centerIn: parent
            }
        }
    }
  • delegate动画
    • onAdd
    • onRemove

示例代码:

javascript 复制代码
delegate: Rectangle {
    required property int index
        
    // 添加动画
    GridView.onAdd: SequentialAnimation {
        NumberAnimation { 
            property: "scale"; from: 0; to: 1; duration: 300 
        }
    }
    
    // 移除动画  
    GridView.onRemove: SequentialAnimation {
        NumberAnimation { 
            property: "scale"; to: 0; duration: 300 
        }
    }
}

二、测试

场景:两个按钮,一个按钮点击,在网格中添加一个元素;另一个按钮点击,删除最后一个元素

javascript 复制代码
import QtQuick

Rectangle{
    anchors.fill: parent

    gradient: Gradient{
        GradientStop{position:0.0; color:"#dbdbdb"}
        GradientStop{position:1.0; color:"#5fc9f8"}
    }

    // model
    ListModel{
        id:theModel
        ListElement{ num:0}
        ListElement{ num:1}
        ListElement{ num:2}
        ListElement{ num:3}
    }

    //button
    Rectangle{
        anchors.bottom: parent.bottom
        anchors.left: parent.left
        anchors.right: parent.right
        anchors.margins: 20

        height: 40
        color: "#53f769"
        border.color: Qt.lighter(color, 1.1)

        Text{
            anchors.centerIn: parent
            text: qsTr("add")
        }

        MouseArea{
            anchors.fill: parent
            onClicked: {
                theModel.append({"num": theModel.count})
                console.log(theModel.count)
            }
        }
    }

    // 删除按钮
    Rectangle{
        anchors.bottom: parent.bottom
        anchors.left: parent.left
        anchors.right: parent.right
        anchors.margins: 20
        anchors.bottomMargin: 60  // 在添加按钮上方

        height: 40
        color: "#f75353"
        border.color: Qt.lighter(color, 1.1)

        Text{
            anchors.centerIn: parent
            text: qsTr("remove last")
        }

        MouseArea{
            anchors.fill: parent
            onClicked: {
                if (theModel.count > 0) {
                    theModel.remove(theModel.count - 1)
                    console.log(theModel.count)
                }
            }
        }
    }

    // view
    GridView{
        anchors.fill: parent
        anchors.margins: 20
        anchors.bottomMargin: 80

        clip: true
        cellHeight: 40
        cellWidth: 60

        model:theModel
        delegate: numberDelegate
    }

    // Delegate
    Component{
        id: numberDelegate
        Rectangle{
            id: wrapper
            required property int index
            required property int num

            gradient: Gradient{
                GradientStop{position:0.0; color:"#dbdbdb"}
                GradientStop{position:1.0; color:"#5fc9f8"}
            }

            width: 40
            height: 25
            color: ListView.isCurrentItem ? "black" : "red"

            Text{
                id:label1
                text: qsTr(wrapper.index)

                anchors.centerIn: parent
            }

            // 添加动画
            GridView.onAdd:addAnimation.start()
            GridView.onRemove:removeAnimation.start()

            NumberAnimation{
                id:addAnimation
                target:wrapper
                property: "scale"
                from: 0
                to:1
                duration: 250
                easing.type: Easing.InOutQuad
            }

            NumberAnimation{
                id:removeAnimation
                target:wrapper
                property: "scale"
                to:0
                duration: 250
                easing.type: Easing.InOutQuad
            }
        }
    }
}
相关推荐
css趣多多7 小时前
动态路由,路由重置,常量路由,$ref,表单验证流程
开发语言·javascript·ecmascript
一只小bit7 小时前
Qt 绘图核心教程:从基础绘制到图像操作全解析
前端·c++·qt·gui
浪潮IT馆7 小时前
在 VSCode 中调试 JavaScript 的 Jest 测试用例
javascript·ide·vscode
u0109272717 小时前
C++中的模板方法模式
开发语言·c++·算法
山上三树7 小时前
详细介绍读写锁
开发语言·c++·spring
Y淑滢潇潇7 小时前
WEB 作业 即时内容发布前端交互案例
前端·javascript·交互
比特森林探险记7 小时前
后端开发者快速入门react
开发语言·前端·javascript
苏宸啊7 小时前
string实现
c++
马士兵教育7 小时前
计算机专业学生入行IT行业,编程语言如何选择?
java·开发语言·c++·人工智能·python
一起养小猫7 小时前
Flutter for OpenHarmony 实战:ListView与GridView滚动列表完全指南
开发语言·javascript·flutter