玩转Qml(3)-换皮肤
本文于
1059
天之前发表,文中内容可能已经过时。
简介 本文是《玩转Qml》系列文章的第三篇,涛哥将教大家,如何在Qml中实现动态换皮肤。顺带会分享一些Qt小技巧。
源码 《玩转Qml》系列文章,配套了一个优秀的开源项目:TaoQuick
github https://github.com/jaredtao/TaoQuick
访问不了或者速度太慢,可以用国内的镜像网站gitee
https://gitee.com/jaredtao/TaoQuick
效果预览 效果类似于网易云音乐
顺便说一下,这是涛哥创建的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,
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 Item { QtObject { id: dataObj property string name: "Hello" onNameChanged: { console.info("name:" , name) } } ... Button { ... onClicked: { dataObj.name = "World" ; } } ... }
(哈哈,工作量和心理负担一下子减轻了很多,头发的数量也能保住了。再也不想回去写Qt/C++的属性了。)
全局单例 涛哥写了一个单独的qml文件,顶层就是一个QtObject类型,里面会有一大堆属性。
颜色、字体一类的配置都在这里。
1 2 3 4 5 6 7 8 9 import QtQuick 2.0 QtObject { property color titleBackground : "#c62f2f" property color background : "#f6f6f6" property color reserverColor : "#ffffff" property color textColor : "black" }
然后在main.qml中实例化它
1 2 3 4 5 6 7 8 9 Item { width : 800 height : 600 GlobalConfig { id: gConfig } ... }
Qml有个特性,子页面实例可以通过id访问父页面中的实例,读 写其属性、调用其函数。
在main.qml中实例化的对象,相当于是全局的了,它的id是可以在所有main.qml的子页面中访问到的。
(当然还有一种方式,通过qmldir指定单例,这种留着后面再说)
实现 皮肤的配置和原理 下面是TaoQuick中使用的gConfig
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 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时重新读属性,修改后的颜色自动就生效了。
这里顺带说一下,主题的颜色相关属性越少越好,因为太多了不容易识别、不好维护,
之前的文章《玩转Qml(1)-从按钮开始》中提到的Qt.lighter和Qt.darker,
就是一种减少颜色属性数量的神器。
皮肤选择器 再来看一下,涛哥参考 网易云音乐 做的皮肤选择器
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 TImageBtn { 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 } } } } } }
带三角形尖尖的弹窗组件 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 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 Rectangle { id: bar visible : popup.visible rotation : 45 width : 16 height : 16 color : barColor anchors.horizontalCenter : parent .horizontalCenter 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 } }