QML 滚动视图:ScrollView、Flickable,以及 ScrollIndicator 用法

2 阅读3分钟

QML 里最常用的滚动方式有:用ScrollView组件直接包起来、在Flickable组件中加上ScrollBar。如果只想显示提示不让人拖动就用ScrollIndicator。

这篇文章用三个 demo 把基础用法过一遍,看完就知道该选哪个。

ScrollView 滚动

一个简单的滚动示例:

1.gif

演示代码

import QtQuick
import QtQuick.Controls
import QtQuick.Layouts

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

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

        ScrollView {
            Layout.fillWidth: true
            Layout.fillHeight: true
            ScrollBar.vertical.policy: ScrollBar.AsNeeded
            ScrollBar.vertical.interactive: true

            ColumnLayout {
                anchors.fill: parent
                anchors.margins: 10

                Repeater {
                    model: 20
                    delegate: ItemDelegate {
                        background: Rectangle {
                            implicitWidth: 150
                            implicitHeight: 25
                            color: "#3498db"
                            radius: 4
                        }
                        contentItem: Text {
                            text: "This is Scroll Item " + index
                            color: "#fff"
                        }
                    }
                }
            }
        }
    }
}

直接使用 ScrollView 比较省事,用户只需要把内容丢进去,滚动条自动就有了。policy: AsNeeded 表示内容装不下时才显示滚动条,放得下就自动藏起来;interactive: true 表示滑块能用鼠标直接拖动。

这个演示demo里使用 Repeater 一次性生成了 20 条条目,把内容撑出可视高度,自然就能上下滚动。适用场景:设置页、长表单、条目数不定的纵向列表。

Flickable 双向滚动

当内容横竖都超出视图时,垂直、水平两条滚动条同时出现。

2.gif

演示代码

import QtQuick
import QtQuick.Controls
import QtQuick.Layouts

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

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

        Flickable {
            id: flickable
            Layout.fillWidth: true
            Layout.fillHeight: true
            clip: true  // 防止内容超出边界

            contentWidth: contentRect.width
            contentHeight: contentRect.height

            ScrollBar.vertical: ScrollBar { policy: ScrollBar.AlwaysOn }
            ScrollBar.horizontal: ScrollBar { policy: ScrollBar.AlwaysOn }

            Rectangle {
                id: contentRect
                width: 500
                height: 500
                color: "#3498db"
                Text {
                    text: "Scrollable Content"
                    anchors.centerIn: parent
                    font.pointSize: 20
                    color: "#fff"
                }
            }
        }
    }
}

Flickable 本身只负责让内容能动起来,滚动条需要自己加上,默认不显示。ScrollBar.vertical 和 ScrollBar.horizontal 这两个附加属性,就是分别对应垂直、水平方向的滚动条。

想让内容滚得动,contentWidth 和 contentHeight 必须设成内容的真实宽高——视口比内容小,才有东西可以滚;clip: true 再把超出框的内容裁掉,不然会露到外面。这里 policy 用了 AlwaysOn,两条滚动条一直显示,方便看效果。

和 ScrollView 比:ScrollView 开箱即用,但能调的东西不多;Flickable 什么都能自己定,代价是滚动条、裁剪这些都得自己动手。

适用场景:画布、大图预览、需要横向纵向都平移的区域。

ScrollIndicator 指示器

只用于进度指示:

3.gif

演示代码

import QtQuick
import QtQuick.Controls
import QtQuick.Layouts

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

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

        Flickable {
            id: flickable
            Layout.fillWidth: true
            Layout.fillHeight: true
            clip: true  // 防止内容超出边界

            contentWidth: contentRect.width
            contentHeight: contentRect.height

            // 与ScrollBar的区别是不可交互,仅提供视觉指示,
            // ScrollIndicator不能用在ScrollView中,
            // 否则报错:ScrollIndicator must be attached to a Flickable.
            ScrollIndicator.vertical: ScrollIndicator {
                contentItem: Rectangle {
                    implicitWidth: 6
                    color: "#ccc"
                    radius: 3
                }
            }

            Rectangle {
                id: contentRect
                width: 500
                height: 500
                color: "#3498db"
                Text {
                    text: "Scrollable Content"
                    anchors.centerIn: parent
                    font.pointSize: 20
                    color: "#fff"
                }
            }
        }
    }
}

ScrollIndicator 和 ScrollBar 长得很像,但它只能看不能拖,是纯视觉提示用的。它还有个硬性限制:只能附加在 Flickable 上,不能写进 ScrollView 里,硬写会直接报错。

新手最容易把 ScrollBar 和 ScrollIndicator 搞混,记一个判断标准就够了:能拖的叫 ScrollBar,只能看的叫 ScrollIndicator。

适用场景:只读浏览、不想让用户拖进度的翻页式内容。

什么场景用哪个

场景用哪个
纵向列表,内容包起来就能滚ScrollView
横竖都要滚,或要自己控制滚动行为Flickable + ScrollBar
只提示位置,不允许拖动ScrollIndicator

已验证环境: