QML Page:把 Page 装进切换容器里

单个 Page 组件的骨架是静态的,界面真正动起来要靠切换容器(StackLayout、SwipeView)。Qt 里最常见的两种切换是「堆栈切换」和「滑动切换」,分别对应 StackLayoutSwipeView。这篇就用两个 demo 把 Page 装进这两个容器,顺带解决一个新手困惑:Pagetitle 到底怎么显示出来。

  • StackLayout 切换 --- 按钮切换索引,页面状态全部保留
  • SwipeView 滑动切换 --- 手势滑动翻页 + PageIndicator 圆点指示

Demo 1:Page 与 StackLayout

将三个 Page 叠加放入 StackLayout 里:

演示代码

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: 5

            Repeater {
                model: ["首页", "列表", "详情"]

                Button {
                    text: modelData
                    highlighted: stackLayout.currentIndex === index
                    onClicked: stackLayout.currentIndex = index
                }
            }
        }

        StackLayout {
            id: stackLayout
            Layout.fillWidth: true
            Layout.fillHeight: true

            // StackLayout 不做页面标题渲染,当前页由上方按钮高亮指示,
            // 故这里不设置 Page.title(title 只对能读取它的宿主有意义)
            Page {
                Label {
                    anchors.centerIn: parent
                    text: "首页页面"
                    font.pointSize: 14
                }
            }

            Page {
                ListView {
                    anchors.fill: parent
                    clip: true
                    model: 5
                    delegate: ItemDelegate {
                        text: "列表项 " + (index + 1)
                    }
                }
            }

            Page {
                Label {
                    anchors.centerIn: parent
                    text: "详情页面(索引 " + stackLayout.currentIndex + ")"
                    font.pointSize: 14
                }
            }
        }

        Item { Layout.fillHeight: true }
    }
}

关键逻辑解析

切换就是改 currentIndex

StackLayout 的所有子项按顺序排成 0、1、2......页,currentIndex 指向谁就显示谁。按钮那行用 Repeater 组件从字符串数组批量生成三个 Button,每个按钮只干两件事:

qml 复制代码
highlighted: stackLayout.currentIndex === index   // 是当前页就高亮
onClicked: stackLayout.currentIndex = index       // 点谁切到谁

不需要中间层信号转发,Button 直接读写 StackLayout 的索引,这是最省事的联动写法。

StackLayout 与 SwipeView 的本质区别

StackLayout 不是导航控件,它只是个「同位置叠放 + 按索引显示」的布局------所有子页同时存在,切换只是改谁可见。带来的特性:页面里的状态(比如列表滚动位置、输入框内容)切换后原样保留;但也意味着没有滑动动画、所有页面一开始就全部实例化。

所以 demo 里在 StackLayout 内放 ListView 很安全,切走再切回来滚动位置还在。

不要在 StackLayout 里设 Page.title

Page 有个 title 属性,但它自己不会渲染标题。

官方文档说得明白,title 是给宿主读取用的(StackViewDialog 这类会读,或者你手动显示它)。StackLayout 不读 title,写了也没人看,反而会让新手误以为「设了 title 就有标题栏」。demo 里索性不设,当前页靠按钮高亮指示就够了。

Demo 2:Page 与 SwipeView

将三个带有颜色的 Page 组件放进 SwipeView 组件中,左右滑动翻页,PageIndicator 显示当前在第几页面,顶部标题会跟随页面切换。

演示代码

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

FadeInAnimation {
    id: demoRoot
    // 桥接 SwipeView 当前页,便于宿主读取 Page.title(Page 自身不渲染标题)
    property var currentPage: swipeView.currentItem

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

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

        // Page 不负责绘制 title,标题由宿主(这里是 Label)读取 currentPage.title 显示
        Label {
            text: demoRoot.currentPage ? demoRoot.currentPage.title : ""
            font.pointSize: 13
            font.bold: true
            color: "#1296FF"
            Layout.alignment: Qt.AlignHCenter
        }

        SwipeView {
            id: swipeView
            Layout.fillWidth: true
            Layout.fillHeight: true
            clip: true

            Page {
                title: "首页"
                background: Rectangle { color: "#3498db" }
                Label {
                    anchors.centerIn: parent
                    text: "首页内容"
                    color: "#fff"
                    font.pointSize: 16
                }
            }

            // ... 第二、三页(发现 / 消息)结构与首页相同,
            //     仅 title、背景色、文字不同 ...

            Page {
                title: "消息"
                background: Rectangle { color: "#2ecc71" }
                Label {
                    anchors.centerIn: parent
                    text: "消息内容"
                    color: "#fff"
                    font.pointSize: 16
                }
            }
        }

        PageIndicator {
            count: swipeView.count
            currentIndex: swipeView.currentIndex
            Layout.alignment: Qt.AlignHCenter
        }

        Item { Layout.fillHeight: true }
    }
}

关键逻辑解析

滑动 + 圆点指示,一行绑定

SwipeView 自带手势翻页,通过以下两个属性就能够和 PageIndicator 组件互动:

qml 复制代码
count: swipeView.count            // 圆点总数
currentIndex: swipeView.currentIndex  // 当前圆点高亮

PageIndicator 只管显示,滑动逻辑全在 SwipeView 里。另外 clip: true 要带上,翻页动画时页面会横向滑出边界,不裁剪就会画到容器外面去。

用 background 给 Page 上色

上一篇讲过内容子项要自己写 Rectangle 填充,这里换了更省事的做法:直接把 Rectangle 塞进 Page.background

qml 复制代码
Page {
    background: Rectangle { color: "#3498db" }
    ...
}

background 会自动铺满整个 Page(含 header/footer 区域),比在内容里垫矩形少一层嵌套。

Page.title 的正确打开方式

这就是 Demo 1 说的「宿主读取 title」的实例。顶部那行蓝色 Label 绑定当前页:

qml 复制代码
text: demoRoot.currentPage ? demoRoot.currentPage.title : ""

currentPageswipeView.currentItem 的桥接属性------注意 currentItem 的静态类型是 Item,直接读 .title 静态检查会报找不到属性,用一个 property var 中转后运行时拿到的是真实的 Page 对象,.title 就有效了。

标题不跟着滑动动画走、只在 currentIndex 切换瞬间更新,够用且简单。

StackLayout 还是 SwipeView

场景 用哪个
顶部 Tab/按钮导航,切换要保留各页状态 StackLayout
移动端手势翻页、左右滑动的引导页 SwipeView

运行验证

  1. 用 Qt Creator 打开 qml_page/CMakeLists.txt
  2. Ctrl+R 运行;
  3. StackLayout demo:点「首页 / 列表 / 详情」按钮看切换与高亮,把列表滚到中间再切走切回,确认滚动位置保留;
  4. SwipeView demo:左右滑动翻页,观察顶部标题和底部圆点同步变化,翻页过程内容不会画出容器边界。

扩展复用方向

  • StackLayout 页面切增加淡入淡出:每个子页外包一层 opacity + Behavior 动画即可。
  • SwipeViewTabBar 是移动 App 底部导航的经典组合:TabBar.currentIndexSwipeView.currentIndex 双向绑定。
  • PageIndicator 换个交互逻辑就能当「轮播图指示器」用,配 Timer 自动翻页就是广告轮播。

已验证环境

相关推荐
秋田君1 小时前
Qt_串口编程
开发语言·qt
励志不掉头发的内向程序员3 小时前
【LibreCAD 2D架构】从两个坐标到图形实体:RS_ActionDrawLine如何创建RS_Line
开发语言·c++·qt·学习·系统架构
似水এ᭄往昔3 小时前
【Qt】--常用控件(按钮类控件)
开发语言·qt
_林枭_14 小时前
ZW3D二次开发_原生表单_如何设置表单左上角图标
qt·zw3d
Quz20 小时前
QML 快捷键篇:基础快捷键与组合键序列
qt·qml
≮傷£≯√1 天前
Qt 面试(一)
qt·ffmpeg·音视频
西西弗Sisyphus1 天前
Qt 分类导航区的实现
开发语言·qt
galaxy_strive1 天前
Qt C++插件化编写项目(1)
开发语言·c++·qt
kaixin_learn_qt_ing2 天前
Qt Model/View
qt