自定义 Qt Quick 控件#
一套用于在 Qt Quick 中创建用户界面的 UI 控件
Qt Quick 控件由一系列项目(树状结构)组成。为了提供自定义的外观和感觉,每个项目默认的 QML 实现可以被自定义实现替换。
自定义控件#
有时您可能需要为您 UI 的某个特定部分创建一个“一次性”的外观,并在其他所有地方使用完整的样式。也许您对您所使用的样式感到满意,但有一个按钮有着特殊的意义。
创建此按钮的第一个方法是直接在需要的位置定义它。例如,如果您不满意基本样式的按钮有方形角落,要使其圆润,可以覆盖 background 项目并将矩形属性设置为半径
注意
由于构成任意风格的控制的不同项目都是设计在一起工作的,可能会需要覆盖其他项目以得到所需的外观。此外,并非所有样式都可以自定义。有关更多信息,请参阅 自定义参考 中的注释。
创建按钮的第二个方法是如果您打算在多个位置使用您圆润的按钮时很好。它涉及到将代码移至项目中的自己的 QML 文件。
为此方法,我们将从基本样式的 Button.qml
中复制背景代码。此文件可以在以下路径中找到您的 Qt 安装中
$QTDIR/qml/QtQuick/Controls/Basic/Button.qml
完成之后,我们只需添加以下行
radius: 4
为了避免与模块本身中的控件混淆,我们将该文件命名为 MyButton.qml
。要使用控件,请按其文件名引用
创建按钮的第三种方法从文件系统的结构和在 QML 中的用途方面都更加结构化。首先,按照上面的方法复制现有文件,但这次将其放置在项目中的一个名为(例如)controls
的子文件夹中。要使用控件,首先将文件夹导入到名称空间中
现在您有了 MyControls
命名空间,您可以按照 Qt Quick 控件模块中实际的对应项来命名控件。您可以为要添加的任何控件重复此过程。
这些三种方法的额外好处是您不需要从头开始实现模板。
注意
这里提到的三种方法不适用于自定义附加的 ToolTip,因为它是内部创建的共享项。要执行 ToolTip
的“一次性”自定义,请参阅 自定义工具提示。要自定义附加的 ToolTip
,它必须作为 您自己的样式的一部分 提供。
创建自定义样式#
创建自己的样式有几种方法。下面,我们将解释各种方法。
样式的定义#
在Qt Quick Controls中,样式本质上是在单个目录内的一系列QML文件。样式要能使用则需要满足四个条件
必须至少有一个QML文件,其名称与控件匹配(例如,
Button.qml
)。每个QML文件必须包含从QtQuick.Templates导入的相关类型作为根项。例如,Button.qml必须包含一个按钮模板作为它的根项。
如果我们像上一节那样使用来自QtQuick.Controls的相应类型,它将不起作用:我们定义的控件将试图从自身继承。
一个qmldir文件必须与QML文件一起存在。以下是提供按钮的样式的简单
qmldir
文件的示例module MyStyle Button 2.15 Button.qml如果您正在使用编译时样式选择,则qmldir还应该导入回退样式
# ... import QtQuick.Controls.Basic auto这也可以用于运行时样式选择,而不使用,例如,
setFallbackStyle()
.这种样式的目录结构如下所示
MyStyle ├─── Button.qml └─── qmldir文件必须在可通过QML导入路径找到的目录中。
例如,如果上面提到的MyStyle目录的路径是
/home/user/MyApp/MyStyle
,那么必须将/home/user/MyApp
添加到QML导入路径。要在MyApp中使用MyStyle,请按名称引用它
./MyApp -style MyStyle
样式名称必须与样式目录的大小写匹配;不支持传递mystyle或MYSTYLE。
默认情况下,样式系统使用基本样式作为未实现控件的回退。要自定义或扩展任何其他内置样式,可以使用QQuickStyle
指定不同的回退样式。
这意味着您可以为您自定义的样式实现尽可能多的控件,并将它们放置在几乎任何位置。这也允许用户为您应用程序创建自己的样式。
在Qt Quick Designer中预览自定义样式#
使用上述方法,可以在Qt Quick Designer中预览自定义样式。为此,确保项目有一个qtquickcontrols2.conf文件,并且存在以下条目
[Controls] Style=MyStyle
有关更多信息,请参阅Flat Style示例 .
样式特定的C++扩展#
有时您可能需要使用C++来扩展您的自定义样式。
使用CMake#
qt_add_qml_module(ACoolItem URI MyItems VERSION 1.0 SOURCES acoolcppitem.cpp acoolcppitem.h )
使用QMake#
CONFIG += qmltypes QML_IMPORT_NAME = MyItems QML_IMPORT_MAJOR_VERSION = 1
如果该类声明的头文件无法从您的项目包含路径中访问,您可能需要修改包含路径,以便生成的注册代码可以编译。
INCLUDEPATH += MyItems
有关更多信息,请参阅从C++定义QML类型和构建QML应用。
如果使用该类型的是应用程序中唯一使用的样式,请通过添加QML_ELEMENT宏并将文件作为QML模块的一部分来使用QML引擎注册该类型
如果使用该类型的样式是应用程序中使用的许多样式之一,考虑将每个样式放入单独的模块。然后将在需要时加载这些模块。
自定义样式的注意事项#
在实现自己的样式和自定义控件时,有一些要点需要注意,以确保您的应用程序尽可能性能出色。
避免为样式实现的项委托分配id#
如定义样式中所述,当您为控件实现自己的样式时,您从该控件的相应模板开始。例如,样式中的Button.qml
将与以下结构类似
当您在应用程序中使用按钮时,将创建和将background
和contentItem
项作为根Button
项的子项。
假设您需要进行一次性的按钮自定义(如自定义控件中所述)
在QML中,这通常会导致默认的background
实现和一次性自定义的background
项被创建。Qt Quick Controls使用一种技术来避免创建这两个项,而只创建自定义的background
,这大大提高了控件创建性能。
这项技术依赖于该项在样式实现中不存在id。如果分配了id,则该技术无法工作,并且将创建这两个项。例如,将id分配给background
或contentItem
可能具有诱惑力,以便文件中的其他对象可以引用这些项
使用此代码时,每次创建具有自定义背景的按钮实例时,都会创建两个背景,从而导致次优的创建性能。
在Qt 5.15之前,旧的无用背景会被删除以释放与之相关的资源。然而,由于控件不拥有项,因此不应删除它们。从Qt 5.15开始,旧项不再被删除,因此backgroundRect
项将比需要的时间长地存在——通常是应用程序退出的时间。虽然旧项将被隐藏,从视觉上从控件中解除父项,并且从可访问性树中删除,但在这种环境中分配id时,应注意这些未使用项的创建时间和内存使用。
避免对自定义项进行命令式赋值#
上述章节中提到的方法仅在第一次声明性分配项时工作,因此命令式赋值会导致孤立的项目。始终尽可能使用声明性绑定在分配自定义项时进行。
不要在QML实现中导入QtQuick.Controls#
在编写控制式样实现的QML时,需要注意不要导入QtQuick.Controls
。这样做将防止QML被QML编译器编译。
实现其他类型使用的类型#
假设您在应用程序中使用滚动视图,并决定要自定义它们的滚动条。直接使用自定义的SnackBar .qml,并让公司的它自动选择一个自定义的 SnackBar。然而,这不会起作用。您必须同时实现 SnackBar .qml 和 公司的 .qml。
附加属性#
通常,一个样式会有一些适用于所有控制的属性或属性。附加属性是扩展QML中项的好方法,无需修改该物品现有的任何C ++。例如,Material 和 Universal 样式都有一个附加主题属性,用于控制项目及其子项目是否以亮色或暗色主题渲染。
作为一个例子,让我们添加一个控制擡高的附加属性。我们的样式将通过投射阴影来显示擡高;擡高越高,阴影越大。
第一步是在Qt Creator中创建一个新的Qt Quick Controls应用程序。创建一个新的Qt Quick Controls应用程序之后,我们添加一个C++类型来存储擡高。由于该类型将用于我们风格支持的所有控件,并且我们可能希望在以后添加其他附加属性,所以我们将其称为MyStyle。以下是
#ifndef MYSTYLE_H #define MYSTYLE_H #include <QObject> #include <QtQml> class MyStyle : public QObject { Q_OBJECT Q_PROPERTY(int elevation READ elevation WRITE setElevation NOTIFY elevationChanged) public: explicit MyStyle(QObject *parent = nullptr); static MyStyle *qmlAttachedProperties(QObject *object); int elevation() const; void setElevation(int elevation); signals: void elevationChanged(); private: int m_elevation; }; QML_DECLARE_TYPEINFO(MyStyle, QML_HAS_ATTACHED_PROPERTIES) #endif // MYSTYLE_H
MyStyle.cpp
:
#include "mystyle.h" MyStyle::MyStyle(QObject *parent) : QObject(parent), m_elevation(0) { } MyStyle *MyStyle::qmlAttachedProperties(QObject *object) { return new MyStyle(object); } int MyStyle::elevation() const { return m_elevation; } void MyStyle::setElevation(int elevation) { if (elevation == m_elevation) return; m_elevation = elevation; emit elevationChanged(); }
MyStyle
类型是特殊的,因为在某种程度上,它不应实例化,而应使用它的附加属性。因此,我们按照以下方式将其在main.cpp
中注册
#include <QGuiApplication> #include <QQmlApplicationEngine> #include "mystyle.h" int main(int argc, char *argv[]) { QGuiApplication app(argc, argv); qmlRegisterUncreatableType<MyStyle>("MyStyle", 1, 0, "MyStyle", "MyStyle is an attached property"); QQmlApplicationEngine engine; // Make the directory containing our style known to the QML engine. engine.addImportPath(":/"); engine.load(QUrl(QLatin1String("qrc:/main.qml"))); return app.exec(); }
然后,我们将从基本样式中的 $QTDIR/qml/QtQuick/Controls/Basic/
将 Button.qml
复制到一个新创建的myproject
文件夹中。将新复制的 Button.qml
添加到 qml.qrc
中,这是我们包含QML文档的资源文件。
接下来,我们给按钮的背景委托添加一个阴影
// ... import QtGraphicalEffects import MyStyle // ... background: Rectangle { // ... layer.enabled: control.enabled && control.MyStyle.elevation > 0 layer.effect: DropShadow { verticalOffset: 1 color: control.visualFocus ? "#330066ff" : "#aaaaaa" samples: control.MyStyle.elevation spread: 0.5 } }
注意,我们:
当擡高为
0
时,不用关心阴影根据按钮是否有焦点来更改阴影的颜色
让阴影的大小取决于擡高
要尝试附加属性,我们在main.qml
中创建一个包含两个按钮的行
其中一个按钮没有擡高,另一个的擡高为 10
。
在配置就绪后,我们可以运行我们的示例。要告诉应用程序使用我们的新样式,我们可将 -style MyStyle
作为应用程序参数传递,但指定使用的样式的 方法有很多。
最终效果
注意,import MyStyle 1.0
语句仅因为使用了 MyStyle
所属的属性。即使我们去掉了导入,两个按钮也会使用我们自定义的样式。
自定义参考#
以下示例代码演示了如何使用与 自定义控件 部分相同的方案,来对 Basic 样式的控件进行自定义。这段代码可以作为实现自定义外观和感觉的起点。
注意
不适用于自定义的样式有 macOS 和 Windows 样式。相反,建议始终在一个所有平台上都有的样式之上自定义控件,例如 Basic 样式、Fusion 样式、Imagine 样式、Material 样式 和 Universal 样式。通过这样做,你可以确保它始终在相同的样子,无论应用程序运行时使用哪个样式。要了解如何使用不同的样式,请参阅 在 Qt Quick 控件中使用样式。或者,您也可以 创建自己的样式。
自定义 ApplicationWindow#
ApplicationWindow 包含一个视觉项目:背景。
import QtQuick import QtQuick.Controls ApplicationWindow { visible: true background: Rectangle { gradient: Gradient { GradientStop { position: 0; color: "#ffffff" } GradientStop { position: 1; color: "#c1bbf9" } } } }
自定义 BusyIndicator#
BusyIndicator 包含两个视觉项目:背景 和 内容项。
import QtQuick import QtQuick.Controls BusyIndicator { id: control contentItem: Item { implicitWidth: 64 implicitHeight: 64 Item { id: item x: parent.width / 2 - 32 y: parent.height / 2 - 32 width: 64 height: 64 opacity: control.running ? 1 : 0 Behavior on opacity { OpacityAnimator { duration: 250 } } RotationAnimator { target: item running: control.visible && control.running from: 0 to: 360 loops: Animation.Infinite duration: 1250 } Repeater { id: repeater model: 6 Rectangle { id: delegate x: item.width / 2 - width / 2 y: item.height / 2 - height / 2 implicitWidth: 10 implicitHeight: 10 radius: 5 color: "#21be2b" required property int index transform: [ Translate { y: -Math.min(item.width, item.height) * 0.5 + 5 }, Rotation { angle: delegate.index / repeater.count * 360 origin.x: 5 origin.y: 5 } ] } } } } }
自定义 CheckBox#
CheckBox 包含三个视觉项目:背景、内容项 和 指示器。
import QtQuick import QtQuick.Controls CheckBox { id: control text: qsTr("CheckBox") checked: true indicator: Rectangle { implicitWidth: 26 implicitHeight: 26 x: control.leftPadding y: parent.height / 2 - height / 2 radius: 3 border.color: control.down ? "#17a81a" : "#21be2b" Rectangle { width: 14 height: 14 x: 6 y: 6 radius: 2 color: control.down ? "#17a81a" : "#21be2b" visible: control.checked } } contentItem: Text { text: control.text font: control.font opacity: enabled ? 1.0 : 0.3 color: control.down ? "#17a81a" : "#21be2b" verticalAlignment: Text.AlignVCenter leftPadding: control.indicator.width + control.spacing } }
自定义 CheckDelegate#
CheckDelegate 由三个视觉项组成:背景,内容项和指示器。
import QtQuick import QtQuick.Controls CheckDelegate { id: control text: qsTr("CheckDelegate") checked: true contentItem: Text { rightPadding: control.indicator.width + control.spacing text: control.text font: control.font opacity: enabled ? 1.0 : 0.3 color: control.down ? "#17a81a" : "#21be2b" elide: Text.ElideRight verticalAlignment: Text.AlignVCenter } indicator: Rectangle { implicitWidth: 26 implicitHeight: 26 x: control.width - width - control.rightPadding y: control.topPadding + control.availableHeight / 2 - height / 2 radius: 3 color: "transparent" border.color: control.down ? "#17a81a" : "#21be2b" Rectangle { width: 14 height: 14 x: 6 y: 6 radius: 2 color: control.down ? "#17a81a" : "#21be2b" visible: control.checked } } background: Rectangle { implicitWidth: 100 implicitHeight: 40 visible: control.down || control.highlighted color: control.down ? "#bdbebf" : "#eeeeee" } }
自定义 ComboBox#
ComboBox 由以下部分组成:背景,内容项,弹出菜单,指示器,以及代理。
pragma ComponentBehavior: Bound import QtQuick import QtQuick.Controls ComboBox { id: control model: ["First", "Second", "Third"] delegate: ItemDelegate { id: delegate required property var model required property int index width: control.width contentItem: Text { text: delegate.model[control.textRole] color: "#21be2b" font: control.font elide: Text.ElideRight verticalAlignment: Text.AlignVCenter } highlighted: control.highlightedIndex === index } indicator: Canvas { id: canvas x: control.width - width - control.rightPadding y: control.topPadding + (control.availableHeight - height) / 2 width: 12 height: 8 contextType: "2d" Connections { target: control function onPressedChanged() { canvas.requestPaint(); } } onPaint: { context.reset(); context.moveTo(0, 0); context.lineTo(width, 0); context.lineTo(width / 2, height); context.closePath(); context.fillStyle = control.pressed ? "#17a81a" : "#21be2b"; context.fill(); } } contentItem: Text { leftPadding: 0 rightPadding: control.indicator.width + control.spacing text: control.displayText font: control.font color: control.pressed ? "#17a81a" : "#21be2b" verticalAlignment: Text.AlignVCenter elide: Text.ElideRight } background: Rectangle { implicitWidth: 120 implicitHeight: 40 border.color: control.pressed ? "#17a81a" : "#21be2b" border.width: control.visualFocus ? 2 : 1 radius: 2 } popup: Popup { y: control.height - 1 width: control.width implicitHeight: contentItem.implicitHeight padding: 1 contentItem: ListView { clip: true implicitHeight: contentHeight model: control.popup.visible ? control.delegateModel : null currentIndex: control.highlightedIndex ScrollIndicator.vertical: ScrollIndicator { } } background: Rectangle { border.color: "#21be2b" radius: 2 } } }
如ComboBox 模型角色所述,ComboBox 支持多种类型的模型。
由于所有模型都提供了一个具有 modelData
的匿名属性,因此以下表达式可以检索所有情况下的正确文本
text: model[control.textRole]
当您提供了一个特定的 textRole
和提供一个具备选中角色的结构化数据的模型时,此表达式是一个常规的属性查找。当提供一个没有结构化数据的模型,例如字符串列表,且没有 textRole
时,此表达式检索 modelData
。
自定义 Dial#
import QtQuick import QtQuick.Controls Dial { id: control background: Rectangle { x: control.width / 2 - width / 2 y: control.height / 2 - height / 2 implicitWidth: 140 implicitHeight: 140 width: Math.max(64, Math.min(control.width, control.height)) height: width color: "transparent" radius: width / 2 border.color: control.pressed ? "#17a81a" : "#21be2b" opacity: control.enabled ? 1 : 0.3 } handle: Rectangle { id: handleItem x: control.background.x + control.background.width / 2 - width / 2 y: control.background.y + control.background.height / 2 - height / 2 width: 16 height: 16 color: control.pressed ? "#17a81a" : "#21be2b" radius: 8 antialiasing: true opacity: control.enabled ? 1 : 0.3 transform: [ Translate { y: -Math.min(control.background.width, control.background.height) * 0.4 + handleItem.height / 2 }, Rotation { angle: control.angle origin.x: handleItem.width / 2 origin.y: handleItem.height / 2 } ] } }
自定义 Drawer#
抽屉可以有一个视觉上的 背景 项目。
background: Rectangle { Rectangle { x: parent.width - 1 width: 1 height: parent.height color: "#21be2b" } }
自定义 Frame#
Frame 由一个视觉项组成:背景。
import QtQuick import QtQuick.Controls Frame { background: Rectangle { color: "transparent" border.color: "#21be2b" radius: 2 } Label { text: qsTr("Content goes here!") } }
自定义 GroupBox#
import QtQuick import QtQuick.Controls GroupBox { id: control title: qsTr("GroupBox") background: Rectangle { y: control.topPadding - control.bottomPadding width: parent.width height: parent.height - control.topPadding + control.bottomPadding color: "transparent" border.color: "#21be2b" radius: 2 } label: Label { x: control.leftPadding width: control.availableWidth text: control.title color: "#21be2b" elide: Text.ElideRight } Label { text: qsTr("Content goes here!") } }
自定义 ItemDelegate#
ItemDelegate 由两个视觉项组成:背景和内容项。
import QtQuick import QtQuick.Controls ItemDelegate { id: control text: qsTr("ItemDelegate") contentItem: Text { rightPadding: control.spacing text: control.text font: control.font color: control.enabled ? (control.down ? "#17a81a" : "#21be2b") : "#bdbebf" elide: Text.ElideRight verticalAlignment: Text.AlignVCenter } background: Rectangle { implicitWidth: 100 implicitHeight: 40 opacity: enabled ? 1 : 0.3 color: control.down ? "#dddedf" : "#eeeeee" Rectangle { width: parent.width height: 1 color: control.down ? "#17a81a" : "#21be2b" anchors.bottom: parent.bottom } } }
自定义 Label#
Label 可以有一个视觉上的 背景 项目。
import QtQuick import QtQuick.Controls Label { text: qsTr("Label") color: "#21be2b" }
自定义分页指示器#
分页指示器(PageIndicator)由一个 背景、内容项 和 代理 组成。
import QtQuick import QtQuick.Controls PageIndicator { id: control count: 5 currentIndex: 2 delegate: Rectangle { implicitWidth: 8 implicitHeight: 8 radius: width / 2 color: "#21be2b" opacity: index === control.currentIndex ? 0.95 : pressed ? 0.7 : 0.45 required property int index Behavior on opacity { OpacityAnimator { duration: 100 } } } }
自定义面板#
面板由一个 背景 组成。
import QtQuick import QtQuick.Controls Pane { background: Rectangle { color: "#eeeeee" } Label { text: qsTr("Content goes here!") } }
自定义弹出窗口#
.. _overviews_customizing-progressbar:
自定义进度条#
进度条(ProgressBar)由两个可视项目组成:背景 和 内容项。
import QtQuick import QtQuick.Controls ProgressBar { id: control value: 0.5 padding: 2 background: Rectangle { implicitWidth: 200 implicitHeight: 6 color: "#e6e6e6" radius: 3 } contentItem: Item { implicitWidth: 200 implicitHeight: 4 // Progress indicator for determinate state. Rectangle { width: control.visualPosition * parent.width height: parent.height radius: 2 color: "#17a81a" visible: !control.indeterminate } // Scrolling animation for indeterminate state. Item { anchors.fill: parent visible: control.indeterminate clip: true Row { spacing: 20 Repeater { model: control.width / 40 + 1 Rectangle { color: "#17a81a" width: 20 height: control.height } } XAnimator on x { from: 0 to: -40 loops: Animation.Infinite running: control.indeterminate } } } } }
如上,内容项也是动画的,以表示一个 不确定的 进度条状态。
自定义单选代理#
单选代理(RadioDelegate)由三个可视项目组成:背景、内容项和指示器。
import QtQuick import QtQuick.Controls RadioDelegate { id: control text: qsTr("RadioDelegate") checked: true contentItem: Text { rightPadding: control.indicator.width + control.spacing text: control.text font: control.font opacity: enabled ? 1.0 : 0.3 color: control.down ? "#17a81a" : "#21be2b" elide: Text.ElideRight verticalAlignment: Text.AlignVCenter } indicator: Rectangle { implicitWidth: 26 implicitHeight: 26 x: control.width - width - control.rightPadding y: parent.height / 2 - height / 2 radius: 13 color: "transparent" border.color: control.down ? "#17a81a" : "#21be2b" Rectangle { width: 14 height: 14 x: 6 y: 6 radius: 7 color: control.down ? "#17a81a" : "#21be2b" visible: control.checked } } background: Rectangle { implicitWidth: 100 implicitHeight: 40 visible: control.down || control.highlighted color: control.down ? "#bdbebf" : "#eeeeee" } }
自定义范围滑块#
RangeSlider 由三个视觉元素组成:背景、第一把手柄 以及 第二把手柄。
import QtQuick import QtQuick.Controls RangeSlider { id: control first.value: 0.25 second.value: 0.75 background: Rectangle { x: control.leftPadding y: control.topPadding + control.availableHeight / 2 - height / 2 implicitWidth: 200 implicitHeight: 4 width: control.availableWidth height: implicitHeight radius: 2 color: "#bdbebf" Rectangle { x: control.first.visualPosition * parent.width width: control.second.visualPosition * parent.width - x height: parent.height color: "#21be2b" radius: 2 } } first.handle: Rectangle { x: control.leftPadding + control.first.visualPosition * (control.availableWidth - width) y: control.topPadding + control.availableHeight / 2 - height / 2 implicitWidth: 26 implicitHeight: 26 radius: 13 color: control.first.pressed ? "#f0f0f0" : "#f6f6f6" border.color: "#bdbebf" } second.handle: Rectangle { x: control.leftPadding + control.second.visualPosition * (control.availableWidth - width) y: control.topPadding + control.availableHeight / 2 - height / 2 implicitWidth: 26 implicitHeight: 26 radius: 13 color: control.second.pressed ? "#f0f0f0" : "#f6f6f6" border.color: "#bdbebf" } }
自定义ScrollBar
import QtQuick import QtQuick.Controls ScrollBar { id: control size: 0.3 position: 0.2 active: true orientation: Qt.Vertical contentItem: Rectangle { implicitWidth: 6 implicitHeight: 100 radius: width / 2 color: control.pressed ? "#81e889" : "#c2f4c6" // Hide the ScrollBar when it's not needed. opacity: control.policy === ScrollBar.AlwaysOn || (control.active && control.size < 1.0) ? 0.75 : 0 // Animate the changes in opacity (default duration is 250 ms). Behavior on opacity { NumberAnimation {} } } }
自定义ScrollIndicator
ScrollIndicator 由两个视觉元素组成:背景 和 内容项。
import QtQuick import QtQuick.Controls ScrollIndicator { id: control size: 0.3 position: 0.2 active: true orientation: Qt.Vertical contentItem: Rectangle { implicitWidth: 2 implicitHeight: 100 color: "#c2f4c6" } }
自定义ScrollView
ScrollView 由一个 背景 元素以及水平和垂直滚动条组成。
ScrollView { id: control width: 200 height: 200 focus: true Label { text: "ABC" font.pixelSize: 224 } ScrollBar.vertical: ScrollBar { parent: control x: control.mirrored ? 0 : control.width - width y: control.topPadding height: control.availableHeight active: control.ScrollBar.horizontal.active } ScrollBar.horizontal: ScrollBar { parent: control x: control.leftPadding y: control.height - height width: control.availableWidth active: control.ScrollBar.vertical.active } background: Rectangle { border.color: control.activeFocus ? "#21be2b" : "#bdbebf" } }
自定义Slider
import QtQuick import QtQuick.Controls Slider { id: control value: 0.5 background: Rectangle { x: control.leftPadding y: control.topPadding + control.availableHeight / 2 - height / 2 implicitWidth: 200 implicitHeight: 4 width: control.availableWidth height: implicitHeight radius: 2 color: "#bdbebf" Rectangle { width: control.visualPosition * parent.width height: parent.height color: "#21be2b" radius: 2 } } handle: Rectangle { x: control.leftPadding + control.visualPosition * (control.availableWidth - width) y: control.topPadding + control.availableHeight / 2 - height / 2 implicitWidth: 26 implicitHeight: 26 radius: 13 color: control.pressed ? "#f0f0f0" : "#f6f6f6" border.color: "#bdbebf" } }
自定义SpinBox
SpinBox 由四个视觉元素组成:背景、内容项、向上指示器 和 向下指示器。
import QtQuick import QtQuick.Controls SpinBox { id: control value: 50 editable: true contentItem: TextInput { z: 2 text: control.textFromValue(control.value, control.locale) font: control.font color: "#21be2b" selectionColor: "#21be2b" selectedTextColor: "#ffffff" horizontalAlignment: Qt.AlignHCenter verticalAlignment: Qt.AlignVCenter readOnly: !control.editable validator: control.validator inputMethodHints: Qt.ImhFormattedNumbersOnly } up.indicator: Rectangle { x: control.mirrored ? 0 : parent.width - width height: parent.height implicitWidth: 40 implicitHeight: 40 color: control.up.pressed ? "#e4e4e4" : "#f6f6f6" border.color: enabled ? "#21be2b" : "#bdbebf" Text { text: "+" font.pixelSize: control.font.pixelSize * 2 color: "#21be2b" anchors.fill: parent fontSizeMode: Text.Fit horizontalAlignment: Text.AlignHCenter verticalAlignment: Text.AlignVCenter } } down.indicator: Rectangle { x: control.mirrored ? parent.width - width : 0 height: parent.height implicitWidth: 40 implicitHeight: 40 color: control.down.pressed ? "#e4e4e4" : "#f6f6f6" border.color: enabled ? "#21be2b" : "#bdbebf" Text { text: "-" font.pixelSize: control.font.pixelSize * 2 color: "#21be2b" anchors.fill: parent fontSizeMode: Text.Fit horizontalAlignment: Text.AlignHCenter verticalAlignment: Text.AlignVCenter } } background: Rectangle { implicitWidth: 140 border.color: "#bdbebf" } }
自定义SplitView
SplitView { id: splitView anchors.fill: parent handle: Rectangle { implicitWidth: 4 implicitHeight: 4 color: SplitHandle.pressed ? "#81e889" : (SplitHandle.hovered ? Qt.lighter("#c2f4c6", 1.1) : "#c2f4c6") } Rectangle { implicitWidth: 150 color: "#444" } Rectangle { implicitWidth: 50 color: "#666" } }
自定义StackView
StackView 可以有一个视觉 背景 元素,并且允许自定义用于推送(push)、弹出(pop)和替换(replace)操作的应用过渡。
import QtQuick import QtQuick.Controls StackView { id: control popEnter: Transition { XAnimator { from: (control.mirrored ? -1 : 1) * -control.width to: 0 duration: 400 easing.type: Easing.OutCubic } } popExit: Transition { XAnimator { from: 0 to: (control.mirrored ? -1 : 1) * control.width duration: 400 easing.type: Easing.OutCubic } } }
自定义SwipeDelegate
SwipeDelegate 由六个视觉元素组成:背景、内容项、指示器、swipe.left
、swipe.right
以及 swipe.behind
。
import QtQuick import QtQuick.Controls SwipeDelegate { id: control text: qsTr("SwipeDelegate") Component { id: component Rectangle { color: SwipeDelegate.pressed ? "#333" : "#444" width: parent.width height: parent.height clip: true Label { text: qsTr("Press me!") color: "#21be2b" anchors.centerIn: parent } } } swipe.left: component swipe.right: component contentItem: Text { text: control.text font: control.font color: control.enabled ? (control.down ? "#17a81a" : "#21be2b") : "#bdbebf" elide: Text.ElideRight verticalAlignment: Text.AlignVCenter Behavior on x { enabled: !control.down NumberAnimation { easing.type: Easing.InOutCubic duration: 400 } } } }
自定义SwipeView
SwipeView 可以有一个视觉 背景元素。导航是通过 内容元素 实现的。
import QtQuick import QtQuick.Controls SwipeView { id: control background: Rectangle { color: "#eeeeee" } }
自定义切换#
import QtQuick import QtQuick.Controls Switch { id: control text: qsTr("Switch") indicator: Rectangle { implicitWidth: 48 implicitHeight: 26 x: control.leftPadding y: parent.height / 2 - height / 2 radius: 13 color: control.checked ? "#17a81a" : "#ffffff" border.color: control.checked ? "#17a81a" : "#cccccc" Rectangle { x: control.checked ? parent.width - width : 0 width: 26 height: 26 radius: 13 color: control.down ? "#cccccc" : "#ffffff" border.color: control.checked ? (control.down ? "#17a81a" : "#21be2b") : "#999999" } } contentItem: Text { text: control.text font: control.font opacity: enabled ? 1.0 : 0.3 color: control.down ? "#17a81a" : "#21be2b" verticalAlignment: Text.AlignVCenter leftPadding: control.indicator.width + control.spacing } }
自定义 SwitchDelegate#
SwitchDelegate 由三个可视化元素组成:背景、内容元素 和 指示器。
import QtQuick import QtQuick.Controls SwitchDelegate { id: control text: qsTr("SwitchDelegate") checked: true contentItem: Text { rightPadding: control.indicator.width + control.spacing text: control.text font: control.font opacity: enabled ? 1.0 : 0.3 color: control.down ? "#17a81a" : "#21be2b" elide: Text.ElideRight verticalAlignment: Text.AlignVCenter } indicator: Rectangle { implicitWidth: 48 implicitHeight: 26 x: control.width - width - control.rightPadding y: parent.height / 2 - height / 2 radius: 13 color: control.checked ? "#17a81a" : "transparent" border.color: control.checked ? "#17a81a" : "#cccccc" Rectangle { x: control.checked ? parent.width - width : 0 width: 26 height: 26 radius: 13 color: control.down ? "#cccccc" : "#ffffff" border.color: control.checked ? (control.down ? "#17a81a" : "#21be2b") : "#999999" } } background: Rectangle { implicitWidth: 100 implicitHeight: 40 visible: control.down || control.highlighted color: control.down ? "#bdbebf" : "#eeeeee" } }
自定义 TabBar#
import QtQuick import QtQuick.Controls TabBar { id: control background: Rectangle { color: "#eeeeee" } TabButton { text: qsTr("Home") } TabButton { text: qsTr("Discover") } TabButton { text: qsTr("Activity") } }
自定义 TextArea#
import QtQuick import QtQuick.Controls TextArea { id: control placeholderText: qsTr("Enter description") background: Rectangle { implicitWidth: 200 implicitHeight: 40 border.color: control.enabled ? "#21be2b" : "transparent" } }
自定义 TextField#
import QtQuick import QtQuick.Controls TextField { id: control placeholderText: qsTr("Enter description") background: Rectangle { implicitWidth: 200 implicitHeight: 40 color: control.enabled ? "transparent" : "#353637" border.color: control.enabled ? "#21be2b" : "transparent" } }
自定义 ToolBar#
ToolBar { id: control background: Rectangle { implicitHeight: 40 color: "#eeeeee" Rectangle { width: parent.width height: 1 anchors.bottom: parent.bottom color: "transparent" border.color: "#21be2b" } } RowLayout { anchors.fill: parent ToolButton { text: qsTr("Undo") } ToolButton { text: qsTr("Redo") } } }
自定义 ToolSeparator#
ToolSeparator 由两个可视化元素组成:背景 和 内容元素。
ToolBar { RowLayout { anchors.fill: parent ToolButton { text: qsTr("Action 1") } ToolButton { text: qsTr("Action 2") } ToolSeparator { padding: vertical ? 10 : 2 topPadding: vertical ? 2 : 10 bottomPadding: vertical ? 2 : 10 contentItem: Rectangle { implicitWidth: parent.vertical ? 1 : 24 implicitHeight: parent.vertical ? 24 : 1 color: "#c3c3c3" } } ToolButton { text: qsTr("Action 3") } ToolButton { text: qsTr("Action 4") } Item { Layout.fillWidth: true } } }
自定义 ToolTip#
注意
要自定义附加的Tooltip,必须将其作为您自己的样式的一部分提供。要一次性自定义一个ToolTip
,请参阅自定义Tooltip。
自定义滚轮#
import QtQuick import QtQuick.Controls Tumbler { id: control model: 15 background: Item { Rectangle { opacity: control.enabled ? 0.2 : 0.1 border.color: "#000000" width: parent.width height: 1 anchors.top: parent.top } Rectangle { opacity: control.enabled ? 0.2 : 0.1 border.color: "#000000" width: parent.width height: 1 anchors.bottom: parent.bottom } } delegate: Text { text: qsTr("Item %1").arg(modelData + 1) font: control.font horizontalAlignment: Text.AlignHCenter verticalAlignment: Text.AlignVCenter opacity: 1.0 - Math.abs(Tumbler.displacement) / (control.visibleItemCount / 2) required property var modelData required property int index } Rectangle { anchors.horizontalCenter: control.horizontalCenter y: control.height * 0.4 width: 40 height: 1 color: "#21be2b" } Rectangle { anchors.horizontalCenter: control.horizontalCenter y: control.height * 0.6 width: 40 height: 1 color: "#21be2b" } }
如果您想要定义自己的内容项,请使用ListView或PathView作为根项。对于包装型滚轮,请使用PathView。
Tumbler { id: tumbler contentItem: PathView { id: pathView model: tumbler.model delegate: tumbler.delegate clip: true pathItemCount: tumbler.visibleItemCount + 1 preferredHighlightBegin: 0.5 preferredHighlightEnd: 0.5 dragMargin: width / 2 path: Path { startX: pathView.width / 2 startY: -pathView.delegateHeight / 2 PathLine { x: pathView.width / 2 y: pathView.pathItemCount * pathView.delegateHeight - pathView.delegateHeight / 2 } } property real delegateHeight: tumbler.availableHeight / tumbler.visibleItemCount } }
对于非包装型滚轮,请使用ListView。
Tumbler { id: tumbler contentItem: ListView { model: tumbler.model delegate: tumbler.delegate snapMode: ListView.SnapToItem highlightRangeMode: ListView.StrictlyEnforceRange preferredHighlightBegin: height / 2 - (height / tumbler.visibleItemCount / 2) preferredHighlightEnd: height / 2 + (height / tumbler.visibleItemCount / 2) clip: true } }