dd759378563 |
2019-05-18 12:37 |
Qml组件化编程3-动态切换皮肤
简介 本文是《Qml组件化编程》系列文章的第三篇,涛哥将教大家,如何在Qml中实现动态换皮肤。顺带会分享一些Qt小技巧。 文章主要发布在涛哥的博客 和 知乎专栏-涛哥的Qt进阶之路。 效果预览 效果类似于网易云音乐
顺便说一下,这是涛哥创建的TaoQuick项目,后续的各种组件、效果会全部集中在这个项目里。 文章中涉及的代码,都会先贴出来。整个工程的代码,在积累到一定程度后,会开放在github上。 必要的基础 可能有读者会疑惑,涛哥前两篇文章还在讲如何封装基础组件,这第三篇直接就来换皮肤?是不是跨度有点大? 其实换皮肤是一个很基础的功能,如果要做最好在项目初期就做起来,后期想要做换皮肤会困难一些(工作量大)。 如果你的项目做了很多组件化的封装,再做换皮肤会轻松一些。 QObject自定义属性 Qml中有一个类型叫QtObject,涛哥非常喜欢使用这个类型。 以前在写Qt/C++代码中的自定义QObject时,经常需要写一些自定义的Q_PROPERTY,以及实现set、get函数、change信号 如果纯手工写,挺累人的,涛哥曾写过自动生成器,相信很多人也写过类似的工具。 后来涛哥发现,QtCreator有自动生成的功能,只要写上Q_PROPERTY那一行,再用右键菜单生成即可, 高效率人士也可以使用快捷键,光标放在Q_PROPERTY上,按Alt + Enter。
有时候需要把set函数的参数改成const T &类型来减少内存拷贝,并把函数实现移动到cpp文件中。(这都是C++的诟病) 然而,在Qml中,有更加方便的QtObject,
- Item {
QtObject { //定义一个QtObject,相当于Qt/c++代码中的QObject id: dataObj property string name: "Hello" //这就定义了一个string类型的属性name,初始值设为"Hello" //定义完了,已经自带了onNameChanged信号。name被重新赋值时会触发信号 //给这个信号写一个处理函数,就相当于连接上了槽函数。 onNameChanged: { console.info("name:", name) } } ... Button { ... onClicked: { //演示: 按钮按下时,修改前面QtObject的name属性 dataObj.name = "World"; } } ... }
(哈哈,工作量和心理负担一下子减轻了很多,头发的数量也能保住了。再也不想回去写Qt/C++的属性了。) 全局单例 涛哥写了一个单独的qml文件,顶层就是一个QtObject类型,里面会有一大堆属性。 颜色、字体一类的配置都在这里。
- // GlobalConfig.qml
import QtQuick 2.0 QtObject { property color titleBackground: "#c62f2f" //标题栏的背景色 property color background: "#f6f6f6" //标题栏之外的部分的背景色 property color reserverColor: "#ffffff" //与背景色相反的对比色 property color textColor: "black" //文本颜色 }
然后在main.qml中实例化它
- // main.qml
Item { width: 800 height: 600 GlobalConfig { id: gConfig } ... }
Qml有个特性,子页面实例可以通过id访问父页面中的实例,读 写其属性、调用其函数。 在main.qml中实例化的对象,相当于是全局的了,它的id是可以在所有main.qml的子页面中访问到的。 (当然还有一种方式,通过qmldir指定单例,这种留着后面再说) 实现 皮肤的配置和原理 下面是TaoQuick中使用的gConfig
- // GlobalConfig.qml
QtObject {
property color titleBackground: "#c62f2f" property color background: "#f6f6f6" property color reserverColor: "#ffffff" property color textColor: "black" property color splitColor: "gray"
property int currentTheme: 0 onCurrentThemeChanged: { var t = themes.get(currentTheme) titleBackground = t.titleBackground background = t.background textColor = t.textColor } readonly property ListModel themes: ListModel { ListElement { name: qsTr("一品红") titleBackground: "#c62f2f" background: "#f6f6f6" textColor: "#5c5c5c" } ListElement { name: qsTr("高冷黑") titleBackground: "#191b1f" background: "#222225" textColor: "#adafb2" } ListElement { name: qsTr("淑女粉") titleBackground: "#faa0c5" background: "#f6f6f6" textColor: "#5c5c5c" } ListElement { name: qsTr("富贵金") titleBackground: "#fed98f" background: "#f6f6f6" textColor: "#5c5c5c" } ListElement { name: qsTr(" 清爽绿") titleBackground: "#58c979" background: "#f6f6f6" textColor: "#5c5c5c" } ListElement { name: qsTr("苍穹蓝") titleBackground: "#67c1fd" background: "#f6f6f6" textColor: "#5c5c5c" } } }
涛哥在所有的Page页面中,相关颜色设置都绑定到gConfig的相应属性上。 那么换皮肤,只需要修改gConfig中的颜色相关属性即可。因为修改属性时会触发change信号,而所有的Page都绑定了 gConfig的属性,会自动在发生change时重新读属性,修改后的颜色自动就生效了。 这里顺带说一下,主题的颜色相关属性越少越好,因为太多了不容易识别、不好维护, 文章1 Qml组件化编程1-按钮的定制与封装 中提到的Qt.lighter和Qt.darker就是一种减少颜色属性数量的神器。 皮肤选择器 再来看一下,涛哥参考 网易云音乐 做的皮肤选择器
- TImageBtn { //图片按钮,参考文章1
width: 20 height: 20 anchors.verticalCenter: parent.verticalCenter imageUrl: containsMouse ? "qrc:/Image/Window/skin_white.png" : "qrc:/Image/Window/skin_gray.png" onClicked: { skinBox.show() } TPopup { //自定义的弹窗,带三角尖尖的那个。 id: skinBox barColor: gConfig.reserverColor backgroundWidth: 280 backgroundHeight: 180 contentItem: GridView { anchors.fill: parent anchors.margins: 10 model: gConfig.themes cellWidth: 80 cellHeight: 80 delegate: Item { width: 80 height: 80 Rectangle { //表示主题色的色块 anchors.fill: parent anchors.margins: 4 height: width color: model.titleBackground } Rectangle { //主题色边框,鼠标悬浮时显示 anchors.fill: parent color: "transparent" border.color: model.titleBackground border.width: 2 visible: a.containsMouse } Text { //主题名字 anchors { left: parent.left bottom: parent.bottom leftMargin: 8 bottomMargin: 8 } color: "white" text: model.name } Rectangle { //右下角圆圈圈,当前选中的主题 x: parent.width - width y: parent.height - height width: 20 height: width radius: width / 2 color: model.titleBackground border.width: 3 border.color: gConfig.reserverColor visible: gConfig.currentTheme === index } MouseArea { //鼠标状态 id: a anchors.fill: parent hoverEnabled: true onClicked: { //切主题操作 gConfig.currentTheme = index } } } } } }
带三角形尖尖的弹窗组件
- // TPopup.qml
import QtQuick 2.9 import QtQuick.Controls 2.5 Item { id: root anchors.fill: parent property alias popupVisible: popup.visible property alias contentItem: popup.contentItem property color barColor: "white" property alias backgroundItem: background property real backgroundWidth: 200 property real backgroundHeight: 160 property color borderColor: barColor property real borderWidth: 0
property real verticalOffset: 20 //矩形旋转45度,一半被toolTip遮住(重合),另一半三角形和ToolTip组成一个带箭头的ToolTip Rectangle { id: bar visible: popup.visible rotation: 45 width: 16 height: 16 color: barColor //水平居中 anchors.horizontalCenter: parent.horizontalCenter //垂直方向上,由ToolTip的y值,决定位置 anchors.verticalCenter: parent.bottom anchors.verticalCenterOffset: verticalOffset } Popup { id: popup width: backgroundWidth height: backgroundHeight background: Rectangle { id: background color: barColor radius: 8 border.color:borderColor border.width: borderWidth } } function show() { popup.x = (root.width - popup.width) / 2 popup.y = root.height + verticalOffset popupVisible = true } function hide() { popupVisible = false } }
转载声明 文章出自涛哥的博客 – 点击这里查看涛哥的博客文章采用 知识共享署名-非商业性使用-相同方式共享 4.0 国际许可协议进行许可, 转载请注明出处, 谢谢合作 © 涛哥 联系方式 请放心联系我,乐于提供咨询服务,也可洽谈商务合作相关事宜。
|
|