QML Flickable:内容滚动与边界行为

想要显示一个较大块的内容时,而界面无法直接呈现,这时候可选择 Flickable 组件,它就是 Qt Quick 里的可滚动区域。

它有两个基本要点,如何让内容滚起来,以及拖到边界时需要怎样的交互方式。

Flickable 基础用法

将一个超过组件大小的长列表,放进 Flickable 里就能上下滚动:

演示代码

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

FadeInAnimation {

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

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

        Flickable {
            id: flick
            Layout.fillWidth: true
            Layout.fillHeight: true
            clip: true
            contentWidth: width
            contentHeight: column.height

            Column {
                id: column
                width: flick.width
                spacing: 5

                Repeater {
                    model: 15
                    Rectangle {
                        width: flick.width
                        height: 35
                        color: index % 2 ? "#e8f4f8" : "#d4eef6"
                        radius: 3

                        Text {
                            anchors.centerIn: parent
                            text: "列表项 " + (index + 1)
                            font.pixelSize: 13
                        }
                    }
                }
            }

            ScrollBar.vertical: ScrollBar {
                policy: ScrollBar.AsNeeded
            }
        }

        Text {
            text: "contentY: " + Math.round(flick.contentY)
            font.pointSize: 10
            color: "#666"
        }
    }
}

Flickable 的使用套路很固定,contentWidth 和 contentHeight 必须告诉它内容有多大------这里内容是一列,contentHeight 直接绑 column.height,内容有多高就能滚多远,contentWidth: width 表示横向不超,只做纵向滚动。

接着用 clip: true 把超出视口的内容裁掉,不然会露到外面。滚动条不是自带的,需要自己加进去。

适用场景:长列表、大图浏览、任何内容超出视口的区域。

Flickable 边界行为

左右两个列表对比:左边能拖过头再回弹,右边拖到边界就停住。

演示代码

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

FadeInAnimation {

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

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

        RowLayout {
            Layout.fillWidth: true
            Layout.fillHeight: true
            spacing: 10

            Rectangle {
                Layout.fillWidth: true
                Layout.fillHeight: true
                color: "#f9f9f9"
                radius: 4
                border.color: "#ddd"

                Column {
                    anchors.fill: parent
                    anchors.margins: 5
                    spacing: 3

                    Text {
                        text: "DragAndOvershootBounds"
                        font.pixelSize: 9
                        font.bold: true
                        color: "#e67e22"
                    }

                    Flickable {
                        id: flick1
                        width: parent.width
                        height: parent.height - 18
                        clip: true
                        contentWidth: width
                        contentHeight: col1.height
                        boundsBehavior: Flickable.DragAndOvershootBounds

                        Column {
                            id: col1
                            width: flick1.width
                            spacing: 3

                            Repeater {
                                model: 12
                                Rectangle {
                                    width: flick1.width
                                    height: 25
                                    color: index % 2 ? "#ffe0b2" : "#fff3e0"
                                    radius: 2

                                    Text {
                                        anchors.centerIn: parent
                                        text: "项目 " + (index + 1)
                                        font.pixelSize: 10
                                    }
                                }
                            }
                        }
                    }
                }
            }

            Rectangle {
                Layout.fillWidth: true
                Layout.fillHeight: true
                color: "#f9f9f9"
                radius: 4
                border.color: "#ddd"

                Column {
                    anchors.fill: parent
                    anchors.margins: 5
                    spacing: 3

                    Text {
                        text: "StopAtBounds"
                        font.pixelSize: 9
                        font.bold: true
                        color: "#27ae60"
                    }

                    Flickable {
                        id: flick2
                        width: parent.width
                        height: parent.height - 18
                        clip: true
                        contentWidth: width
                        contentHeight: col2.height
                        boundsBehavior: Flickable.StopAtBounds

                        Column {
                            id: col2
                            width: flick2.width
                            spacing: 3

                            Repeater {
                                model: 12
                                Rectangle {
                                    width: flick2.width
                                    height: 25
                                    color: index % 2 ? "#c8e6c9" : "#e8f5e9"
                                    radius: 2

                                    Text {
                                        anchors.centerIn: parent
                                        text: "项目 " + (index + 1)
                                        font.pixelSize: 10
                                    }
                                }
                            }
                        }
                    }
                }
            }
        }

        Text {
            text: "左侧可拖拽超出边界回弹,右侧到达边界即停止"
            font.pointSize: 10
            color: "#666"
        }
    }
}

滚动手感由 boundsBehavior 一个属性决定,取值就这两种:DragAndOvershootBounds 是默认值,拖到边界还能继续拖出去一点,松手自动回弹,移动端列表基本都是这种手感;StopAtBounds 则是一到边界就停,不能越界,桌面工具里更干脆。

两个 Flickable 的核心配置只有一行 boundsBehavior 不同,其余属性完全一样(为了让对比直观,特意给两个列表配了不同的颜色)。

简而言之,想让滚动有弹性,用默认的 DragAndOvershootBounds;想要严格的边界,就写 StopAtBounds。它不影响滚动本身,只决定到边界那一刻的表现。

适用场景:移动端卡片列表(回弹手感)、桌面严格列表(即停)。


已验证环境:

相关推荐
zhanghaha131412 小时前
Qt 类本质就是 C++ 的类,只是 Qt 框架扩展了一套【QObject 对象模型】
qt
Cx330❀19 小时前
深入浅出 Qt 绘图机制:从 QPainter 到四大图像类解析
开发语言·qt·ui·性能优化·图形渲染
Littlehero_12121 小时前
QT自定义控件之水泥厂组态(源码开源)
开发语言·qt
代钦塔拉1 天前
【C++/Qt 进阶】设计模式系列(6):桥接模式——把“两个独立变化的维度“拆开
c++·qt·设计模式
156082072191 天前
银河麒麟 Qt 可执行文件打包说明
开发语言·驱动开发·qt
Quz1 天前
QML GridView 布局:网格排布与排列方向
qt
Quz1 天前
QML GridView 动态:增删过渡与滚动定位
qt
沫璃染墨2 天前
《Qt从零入门系列(十四):QThread多线程详解——从线程创建到客户端阻塞处理》
开发语言·c++·笔记·qt·ui·交互·线程
代钦塔拉2 天前
【C++/Qt 进阶】设计模式系列(5):适配器模式——给“不兼容的接口“贴一层翻译膜
c++·qt·设计模式
把我格子衫拿来2 天前
VTK 9.5 与 Qt 6 联合开发:从嵌入三维窗口到可维护的桌面架构
c++·python·qt·vtk·三维可视化