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。它不影响滚动本身,只决定到边界那一刻的表现。

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


已验证环境:

相关推荐
Quz1 小时前
QML Flipable:卡片翻转与登录设置
qt
阿钱真强道9 小时前
24 嵌入式操作系统 | 项目启动与环境准备(PyQt5 / Flask 两个“最小可跑“)
开发语言·qt·flask·架构设计·协议契约
物极必反102412 小时前
QT使用FFMpeg编译安卓程序.md
qt·ffmpeg·安卓
励志不掉头发的内向程序员16 小时前
【从零写一个CAD 03】三个 double 值得单独一个类吗:把视图变换抽成 View
开发语言·c++·qt·学习·系统架构
sycmancia1 天前
Qml——第一个qml的程序
qt
实心儿儿1 天前
Qt — Qt 多线程
开发语言·qt
沫璃染墨1 天前
《Qt从零入门系列(十一):Qt事件机制详解——从QEvent到鼠标、键盘与定时器事件》
c++·qt·ui·硬件工程·交互·个人开发·qt5
实心儿儿1 天前
Qt — Qt 窗口
开发语言·qt
Quz1 天前
QML 滚动条:自定义滚动样式与滚动逻辑
qt