QML Loader:组件切换与属性传递

加载只是第一步,真正用起来还要解决两件事:组件根据条件来回切,以及把外层数据送进加载出来的实例里。这篇两个示例分别演示声明式切换和实例传参。

  • 切换组件 --- 一个 Switch 开关,在两个内联组件之间条件切换
  • 属性传递 --- 把输入框文字和选中的颜色写进已加载实例的自定义属性,实例即时刷新

切换组件:开关决定显示哪个

拨动开关,面板在红色「组件 1」和蓝色「组件 2」之间切换,底部文字跟着变。

演示代码

qml 复制代码
import QtQuick
import QtQuick.Controls
import QtQuick.Layouts

FadeInAnimation {
    ColumnLayout {
        anchors.fill: parent
        anchors.margins: 20
        spacing: 15

        // ... 省略标题组件 TitleSeparator ...

        Switch {
            id: controlSwitch
            text: "切换组件"
        }

        Rectangle {
            Layout.fillWidth: true
            Layout.fillHeight: true
            color: Qt.rgba(0.95, 0.95, 0.95, 1)
            radius: 4

            Loader {
                anchors.fill: parent
                anchors.margins: 10
                sourceComponent: controlSwitch.checked ? component1 : component2
            }
        }

        Text {
            text: "当前: " + (controlSwitch.checked ? "组件 1(红色)" : "组件 2(蓝色)")
            font.pointSize: 11
            color: "#666"
        }
    }

    Component {
        id: component1
        Rectangle {
            color: "#e74c3c"
            radius: 6

            Text {
                anchors.centerIn: parent
                text: "组件 1"
                color: "#fff"
                font.pointSize: 16
                font.bold: true
            }
        }
    }

    Component {
        id: component2
        Rectangle {
            color: "#3498db"
            radius: 6

            Text {
                anchors.centerIn: parent
                text: "组件 2"
                color: "#fff"
                font.pointSize: 16
                font.bold: true
            }
        }
    }
}

关键逻辑解析

sourceComponent 可以绑表达式,这是声明式切换的核心controlSwitch.checked ? component1 : component2 是一个普通绑定,开关状态一变,Loader 立刻换用另一个模板------不需要 onToggled 里手动赋值,状态和界面始终同步。

切换的代价是旧实例被销毁 。和上一类换 source 一样,切到组件 2 时组件 1 的实例销毁,组件 1 里如果有输入框、滚动位置这类内部状态,会全部丢失。需要保留状态就不能用这种"销毁重建"的切换,得让两个组件常驻再用 visible 控制。

两个组件模板都写在文件底部Component 不是界面元素,放根元素下当"模板库"用即可。页面里只出现 Loader,同一时刻只有当前选中的模板被实例化。

相对路径方案对比 :如果两个组件是独立文件,用 loader.source = checked ? "a.qml" : "b.qml" 效果等价。内联 Component 适合两个模板都很短、只在本页用的场景;文件版适合组件较大、需要单独维护的场景。

属性传递:把数据写进加载的实例

输入框填文字、下拉框选颜色,点「应用」,面板里的实例文字和底色立刻更新;点「重置」恢复默认。

演示代码

qml 复制代码
import QtQuick
import QtQuick.Controls
import QtQuick.Layouts

FadeInAnimation {
    ColumnLayout {
        anchors.fill: parent
        anchors.margins: 20
        spacing: 15

        // ... 省略标题组件 TitleSeparator ...

        RowLayout {
            Layout.fillWidth: true
            spacing: 10

            TextField {
                id: textInput
                placeholderText: "输入文字"
                Layout.fillWidth: true
                maximumLength: 10
            }

            ComboBox {
                id: colorPicker
                Layout.fillWidth: true
                model: [
                    { text: "红色", color: "#e74c3c" },
                    { text: "蓝色", color: "#3498db" },
                    { text: "绿色", color: "#2ecc71" },
                    { text: "紫色", color: "#9b59b6" }
                ]
                textRole: "text"
                currentIndex: 0
            }

            Button {
                text: "应用"
                onClicked: {
                    if (loader.item) {
                        loader.item.displayText = textInput.text || "Hello"
                        loader.item.displayBgColor = colorPicker.model[colorPicker.currentIndex].color
                    }
                }
            }

            Button {
                text: "重置"
                onClicked: {
                    textInput.text = ""
                    colorPicker.currentIndex = 0
                    if (loader.item) {
                        loader.item.displayText = "Hello"
                        loader.item.displayBgColor = colorPicker.model[colorPicker.currentIndex].color
                    }
                }
            }
        }

        Rectangle {
            Layout.fillWidth: true
            Layout.fillHeight: true
            color: Qt.rgba(0.95, 0.95, 0.95, 1)
            radius: 4

            Loader {
                id: loader
                anchors.fill: parent
                anchors.margins: 10
                sourceComponent: displayComponent
                onLoaded: {
                    item.displayText = textInput.text || "Hello"
                    item.displayBgColor = colorPicker.model[colorPicker.currentIndex].color
                }
            }
        }
    }

    Component {
        id: displayComponent
        Rectangle {
            id: inner
            property string displayText: "Hello"
            property color displayBgColor: "#e74c3c"
            color: inner.displayBgColor
            radius: 6
            opacity: 0.9

            Text {
                anchors.centerIn: parent
                text: inner.displayText
                color: "#fff"
                font.pixelSize: 24
                font.bold: true
            }
        }
    }
}

关键逻辑解析

被加载的组件要定义自己的"入参"displayComponent 里声明了 property string displayTextproperty color displayBgColor,这两个自定义属性就是组件对外的接口。外层要传什么,组件就暴露什么属性,耦合只存在于这层约定上。

访问实例统一走 loader.item 。加载进来的实例挂在 Loader.item 上,loader.item.displayText = ... 就是给实例的属性赋值,实例内部的绑定(color: inner.displayBgColortext: inner.displayText)自动刷新,不用手动通知。

onLoaded 负责初始化,按钮负责更新sourceComponent 一开始就非空,Loader 加载完成立刻触发 onLoaded,在这里用当前输入框的值做一次初始化;之后点「应用」再改。两处写的是同一段赋值逻辑,保证"初始态"和"改后态"走同一条路。

赋值前先判空loader.item 在实例未加载时是 null,直接访问会报错,所以 if (loader.item) 包一层。这里 sourceComponent 恒定非空,其实实例常驻,判空是防御写法------换成按需 source 加载时,这个判空就是必需的了。

传值途径不止一种loader.item.xxx = value 适合"加载完成后随时改";如果要在加载的一瞬间 带上初始属性,用 Loader.setSource("file.qml", {prop: value}) 更直接,属性会作为初始值注入实例,比加载完再补一次赋值干净。

sourceComponent 条件切换和属性传递怎么配合用

手段 解决的问题 写法
sourceComponent: a ? comp1 : comp2 两个组件按条件二选一 声明式绑定,免手动赋值
loader.source = checked ? "a.qml" : "b.qml" 两个文件按条件二选一 同上,模板放文件
loader.item.prop = value 实例已存在,随时改数据 赋值即刷新
Loader.setSource(url, {prop: value}) 加载瞬间注入初始值 一次性传参
组件内 property 声明 定义组件对外的接口 组件自己暴露

单页面多形态用 sourceComponent 条件切换;需要把外部数据灌进实例,用 loader.item 赋值或 setSource 初值。数据流方向要固定:外层通过属性单向送数据进组件,组件内部不反向改外层状态,逻辑才不会绕。

运行验证

  1. Qt Creator 打开 qml_loader/CMakeLists.txt,按 Ctrl+R 运行;
  2. 左侧点「切换组件」,拨动开关看红蓝组件切换;
  3. 点「属性传递」,输入文字、选个颜色点「应用」,看面板实例更新;点「重置」恢复 Hello。

扩展复用方向

  • 把多个内联 Component 放进一个 Repeater 或数组,用 sourceComponent 按当前模式取值,可以扩展成"主题切换";
  • 属性传递配合 Connections 反向收信号:实例里发信号,外层 Connections { target: loader.item } 接收,实现双向通信;
  • 组件接口属性多了以后,把 displayText/displayBgColor 这类收拢成一组 property 并加注释,等于一份轻量文档,团队协作时不易传错参数。

已验证环境

相关推荐
老赵的博客1 小时前
c++QT之动态库加载常见报错
c++·qt
Quz1 天前
QML Loader:从文件加载组件、加载内联组件
qt
Quz1 天前
QML Loader: 状态监听与动态标签页
qt
似水এ᭄往昔1 天前
【Qt】--常用控件(显示类控件)
开发语言·qt
慧都小项1 天前
从 Figma 到 Qt:Qtitan 能减少哪些界面还原工作?
前端·qt·嵌入式·figma·图形界面·工业界面·qtitan
用户122003879001 天前
Qt学习之绘图入门练习
qt
西西弗Sisyphus1 天前
Qt 实现一个 Windows 启动项查看器
开发语言·windows·qt
郝学胜-神的一滴2 天前
Qt 高级编程 045:坐标体系深度实战
开发语言·c++·windows·python·qt·程序人生
爱吃巧克力的程序媛2 天前
main.cpp注册 C++ 类到 QML 元对象系统
c++·qt