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

已验证环境:

相关推荐
Quz11 分钟前
QML GridView 布局:网格排布与排列方向
qt
Quz22 分钟前
QML GridView 动态:增删过渡与滚动定位
qt
沫璃染墨1 天前
《Qt从零入门系列(十四):QThread多线程详解——从线程创建到客户端阻塞处理》
开发语言·c++·笔记·qt·ui·交互·线程
代钦塔拉1 天前
【C++/Qt 进阶】设计模式系列(5):适配器模式——给“不兼容的接口“贴一层翻译膜
c++·qt·设计模式
把我格子衫拿来1 天前
VTK 9.5 与 Qt 6 联合开发:从嵌入三维窗口到可维护的桌面架构
c++·python·qt·vtk·三维可视化
把我格子衫拿来2 天前
VTK 与 Qt 的 CMake 工程怎么组织?模块、工具链与二进制兼容排错
c++·qt·vtk·cmake·工程实践
代钦塔拉2 天前
【C++/Qt 进阶】设计模式系列(4):原型模式——用“克隆“代替“重新构造“
c++·qt·设计模式
Python图像识别2 天前
56-【2027毕设】YOLO11X光安检危险物品检测系统 - Python完整源码+PyQt5界面+训练模型+数据集
python·qt·课程设计
把我格子衫拿来2 天前
鼠标点到哪里才算选中?Qt 与 VTK 的拾取坐标、DPI 与体素定位
python·qt·vtk·坐标变换·三维交互
Lhan.zzZ2 天前
Qt WebEngineView 在 Linux 上无法加载网页:从“看不见 HTML“到“禁用沙箱“
linux·c++·qt