QML SplitView:可拖拽分隔面板与自定义 Handle

有时候软件上的内容区域需要用户手动设置比例,SplitView 组件就是解决这个需求的。这篇文章用基础demo介绍概念,再给 Handle(拖拽手柄) 换个好看的样式。

SplitView 基础用法

拖中间的分隔条可以调整大小,支持切换横竖方向:

演示代码

qml 复制代码
pragma ComponentBehavior: Bound
import QtQuick
import QtQuick.Controls
import QtQuick.Controls.Basic
import QtQuick.Layouts

FadeInAnimation {
    id: root
    property color mainColor: Theme.listColors[Theme.demo1]

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

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

        Rectangle {
            Layout.fillWidth: true
            Layout.preferredHeight: 50
            color: Theme.demoStyle.frameBg
            radius: 6
            border.color: Theme.demoStyle.frameBorder
            border.width: 1

            RowLayout {
                anchors.fill: parent
                anchors.margins: 10
                spacing: 10

                Text {
                    text: "当前方向:"
                    font.pointSize: 10
                    color: Theme.demoStyle.frameText
                }

                Text {
                    text: splitView.orientation === Qt.Horizontal ? "水平" : "垂直"
                    font.pointSize: 10
                    color: root.mainColor
                    font.bold: true
                }

                Item { Layout.fillWidth: true }

                CustomButton {
                    text: "切换方向"
                    onClicked: {
                        if (splitView.orientation === Qt.Horizontal) {
                            splitView.orientation = Qt.Vertical
                        } else {
                            splitView.orientation = Qt.Horizontal
                        }
                    }
                }
            }
        }

        Rectangle {
            Layout.fillWidth: true
            Layout.fillHeight: true
            color: Theme.demoStyle.frameBg
            border.color: Theme.demoStyle.frameBorder
            border.width: 1
            radius: 6

            SplitView {
                id: splitView
                anchors.fill: parent
                anchors.margins: 10
                orientation: Qt.Horizontal

                handle: Rectangle {
                    implicitWidth: 6
                    implicitHeight: 6
                    color: root.mainColor
                    opacity: SplitHandle.pressed ? 1.0 : 0.7
                }

                // 面板1
                CustomRect {
                    SplitView.minimumWidth: 80
                    SplitView.minimumHeight: 80
                    rectColor: Qt.color(root.mainColor).darker(1.0)
                    rectNum: "1"
                }

                // 面板2
                CustomRect {
                    SplitView.minimumWidth: 80
                    SplitView.minimumHeight: 80
                    rectColor: Qt.color(root.mainColor).darker(1.2)
                    rectNum: "2"
                }
            }
        }
    }
}

说明: 这个 Demo 用到了工程里的几个共享组件,Theme 是颜色主题单例,CustomRect 是带编号的面板,CustomButton 是统一风格的按钮。它们只负责外观,不影响 SplitView 的核心逻辑,重点看 SplitView 本身。

SplitView的用法很简单,内部的子项自动排成一行或一列,拖动中间的分隔条时两侧面板跟着改变大小。想让面板保留最小空间,就在面板上写 SplitView.minimumWidth / SplitView.minimumHeight 附加属性。

切换方向的实现:

运行时修改 orientation 属性的值就可以了,水平变垂直 或者 垂直变水平,面板排列和分隔条方向都会自动跟着重排。

handle 指的是分隔条组件,是用户可以手动拖拽的部分。

自定义 Handle 样式

如图所示:

演示代码

qml 复制代码
pragma ComponentBehavior: Bound
import QtQuick
import QtQuick.Controls
import QtQuick.Layouts

FadeInAnimation {
    id: root
    property color mainColor: Theme.listColors[Theme.demo2]

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

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

        Rectangle {
            Layout.fillWidth: true
            Layout.preferredHeight: 45
            color: Theme.demoStyle.frameBg
            radius: 6
            border.color: Theme.demoStyle.frameBorder
            border.width: 1

            RowLayout {
                anchors.fill: parent
                anchors.margins: 10
                spacing: 10

                Text {
                    text: "Handle样式:"
                    font.pointSize: 10
                    color: Theme.demoStyle.frameText
                }

                Text {
                    text: "渐变色 + 圆角"
                    font.pointSize: 10
                    color: root.mainColor
                    font.bold: true
                }

                Item { Layout.fillWidth: true }
            }
        }

        Rectangle {
            Layout.fillWidth: true
            Layout.fillHeight: true
            color: Theme.demoStyle.frameBg
            border.color: Theme.demoStyle.frameBorder
            border.width: 1
            radius: 6

            SplitView {
                id: splitView
                anchors.fill: parent
                anchors.margins: 10
                orientation: Qt.Horizontal

                handle: Rectangle {
                    id: handle
                    implicitWidth: 8
                    implicitHeight: 8
                    radius: 4

                    gradient: Gradient {
                        GradientStop { position: 0.0; color: root.mainColor }
                        GradientStop { position: 0.5; color: Qt.color(root.mainColor).darker(1.2) }
                        GradientStop { position: 1.0; color: root.mainColor }
                    }

                    opacity: splitView.resizing ? 1.0 : 0.3

                    // handle正中间
                    Rectangle {
                        width: splitView.orientation === Qt.Horizontal ? parent.width - 4 : 30
                        height: splitView.orientation === Qt.Horizontal ? 30 : parent.height - 4
                        radius: 4
                        color: Theme.demoStyle.handle
                        anchors.centerIn: parent
                    }
                }

                // 面板1
                CustomRect {
                    SplitView.minimumWidth: 80
                    SplitView.minimumHeight: 80
                    rectColor: Qt.color(root.mainColor).darker(1.0)
                    rectNum: "1"
                }

                // 面板2
                CustomRect {
                    SplitView.minimumWidth: 80
                    SplitView.minimumHeight: 80
                    rectColor: Qt.color(root.mainColor).darker(1.2)
                    rectNum: "2"
                }
            }
        }
    }
}

SplitView 的 handle 属性可以整个换掉,这里把分隔条重写成一条带圆角的渐变矩形。

opacity 属性绑的是 splitView.resizing,只有正在拖动分隔条时才完全显示,平时处于半透明状态,好处是不会抢占视觉焦点。

有个细节要注意:handle 里那个把手矩形的宽高是根据 splitView.orientation 算的,水平布局时横着摆、垂直布局时竖着摆,这样不管分隔条方向怎么变,把手都保持居中。改 handle 样式时保留这种方向判断,横竖切换才不会错位。


已验证环境:

相关推荐
Quz1 小时前
QML SplitView 进阶:嵌套布局与状态持久化
qt
时间的拾荒人2 小时前
Qt 显示类控件详解:从 QLabel 到 QCalendarWidget 的实战指南
开发语言·qt·面试
kaixin_learn_qt_ing10 小时前
Qt插件机制demo示例
qt
Quz13 小时前
QML Flickable:内容滚动与边界行为
qt
Quz13 小时前
QML Flipable:卡片翻转与登录设置
qt
阿钱真强道20 小时前
24 嵌入式操作系统 | 项目启动与环境准备(PyQt5 / Flask 两个“最小可跑“)
开发语言·qt·flask·架构设计·协议契约
物极必反10241 天前
QT使用FFMpeg编译安卓程序.md
qt·ffmpeg·安卓
励志不掉头发的内向程序员1 天前
【从零写一个CAD 03】三个 double 值得单独一个类吗:把视图变换抽成 View
开发语言·c++·qt·学习·系统架构
sycmancia2 天前
Qml——第一个qml的程序
qt