QML 滚动条:自定义滚动样式与滚动逻辑

默认的滚动条样式不满足要求,就得自己手动实现一个自定义样式的滚动条,必须重写 background 和 contentItem 属性,即实现自定义的背景、前景组件。

另一种情况是内容根本不在传统的滚动视图(ScrollView、Flickable)里,但滚动条也要能用。这时直接实例化 ScrollBar,用 position 实现内容偏移。

自定义滚动条样式

演示代码

qml 复制代码
import QtQuick
import QtQuick.Controls
import QtQuick.Controls.Basic
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 {
                id: vBar
                width: hovered || pressed ? 14 : 10
                policy: ScrollBar.AsNeeded

                Behavior on width {
                    NumberAnimation { duration: 150; easing.type: Easing.OutCubic }
                }

                // 轨道:内嵌浅槽
                background: Rectangle {
                    implicitWidth: 10
                    radius: width / 2
                    color: "#1296FF"
                    opacity: 0.10
                    border.width: 1
                    border.color: "#2E1296FF"
                }

                // 滑块:胶囊形 + 主题色渐变,悬停时加宽提亮
                contentItem: Rectangle {
                    implicitWidth: 8
                    radius: width / 2
                    gradient: Gradient {
                        GradientStop {
                            position: 0.0
                            color: vBar.pressed ? "#0A6FB8" : vBar.hovered ? "#4DB3FF" : "#8FCEFF"
                        }
                        GradientStop {
                            position: 1.0
                            color: vBar.pressed ? "#0A7ACC" : vBar.hovered ? "#1296FF" : "#5FB9FF"
                        }
                    }
                    opacity: vBar.pressed ? 1.0 : vBar.hovered ? 0.95 : 0.8
                    Behavior on opacity {
                        NumberAnimation { duration: 150 }
                    }
                }
            }

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

自定义滚动条外观就两块:background 画轨道,contentItem 画滑块。

轨道是一条半透明的圆角槽,滑块窄一点嵌在里面,用渐变色填充。滚动条的 width 属性绑定了 hovered(滑过) 和 pressed(按压) 状态。

适用场景:品牌色滚动条、深色主题、需要悬停反馈的列表。

自定义滚动逻辑

直接将滚动条挂在普通 Rectangle 上,推动文字平移。

演示代码

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

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

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

        Rectangle {
            id: frame
            Layout.fillWidth: true
            Layout.fillHeight: true
            border.width: 1
            border.color: "#ccc"
            clip: true

            Text {
                id: content
                text: "ScrollBar In Rectangle"
                font.pixelSize: frame.height
                x: -hbar.position * width
                y: -vbar.position * height
                rightPadding: 20
                bottomPadding: 20
            }

            ScrollBar {
                id: vbar
                hoverEnabled: true
                active: hovered || pressed
                orientation: Qt.Vertical
                size: frame.height / content.height
                anchors.top: parent.top
                anchors.right: parent.right
                anchors.bottom: parent.bottom
                anchors.margins: 1
            }

            ScrollBar {
                id: hbar
                hoverEnabled: true
                active: hovered || pressed
                orientation: Qt.Horizontal
                size: frame.width / content.width
                anchors.left: parent.left
                anchors.right: parent.right
                anchors.bottom: parent.bottom
                anchors.margins: 1
            }
        }
    }
}

ScrollBar 不只能附加在传统的滚动视图中,也可以当普通组件直接创建。

该模式下核心是两个值:size 是滑块占轨道的比例,算法很简单,可视区高度除以内容总高度;position 是滑块当前位置,取值 0 到 1。

内容的 x、y 坐标绑成 -position * 宽高。滑块往右拖,内容就往左移,方向正好相反,所以前面要加个负号。

active: hovered || pressed 的意思是:平时滚动条淡下去,鼠标碰上或按住时才亮起来。hoverEnabled: true 是悬停检测的开关,不开的话 hovered 永远是 false。水平和垂直滚动条分别锚到容器的边上,位置就固定了。

这种写法的好处是轻量化,不用引入 Flickable,滚动逻辑完全自己说了算。代价是所有滚动行为都得手动接,包括鼠标滚轮、惯性滑动这些 Flickable 自带的能力。

适用场景:自绘视口、只读取滚动位置的场景。

两个方案怎么选

需求 用哪个
内容已在 Flickable / ScrollView 里,只改外观 附加 ScrollBar,重写 background / contentItem
内容不在 Flickable 里,要自己接滚动逻辑 直接实例化 ScrollBar,绑 size 和 position

已验证环境:

相关推荐
Quz1 小时前
QML 滚动视图:ScrollView、Flickable,以及 ScrollIndicator 用法
qt
小许同学记录成长2 小时前
几何体编辑算法
qt·算法
时间的拾荒人2 小时前
Qt 信号与槽机制详解(二):连接方式详解与面试指南
开发语言·qt·面试
实心儿儿12 小时前
Qt — Qt 事件
开发语言·qt
bkspiderx19 小时前
Qt 的消息机制:事件驱动与信号槽的底层逻辑
开发语言·qt·信号槽·事件驱动·qt 的消息机制
江湖人称菠萝包20 小时前
【Qt】《Qt 5.9 C++开发指南》笔记-Chapter11-数据库
笔记·qt·qt5
励志不掉头发的内向程序员21 小时前
【从零写一个CAD 02】画完第一条线之后:实体容器、Esc 取消,和按了没反应的键盘
开发语言·c++·qt·学习·系统架构
Quz1 天前
QML TreeView:排序与搜索过滤
qt
Quz1 天前
QML TreeView:基础树视图与自定义委托
qt