• 1842阅读
  • 2回复

Qml组件化编程3-动态切换皮肤 [复制链接]

上一主题 下一主题
离线dd759378563
 

只看楼主 倒序阅读 楼主  发表于: 2019-05-18

简介

本文是《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,
  1. Item {
  2.     QtObject {  //定义一个QtObject,相当于Qt/c++代码中的QObject
  3.         id: dataObj
  4.         property string name: "Hello"   //这就定义了一个string类型的属性name,初始值设为"Hello"
  5.         
  6.         //定义完了,已经自带了onNameChanged信号。name被重新赋值时会触发信号
  7.         
  8.         //给这个信号写一个处理函数,就相当于连接上了槽函数。
  9.         onNameChanged: {
  10.             console.info("name:", name)
  11.         }
  12.     }
  13.     ...
  14.     Button {
  15.         ...
  16.         onClicked: {    //演示: 按钮按下时,修改前面QtObject的name属性
  17.             dataObj.name = "World";
  18.         }
  19.     }
  20.     ...
  21. }



(哈哈,工作量和心理负担一下子减轻了很多,头发的数量也能保住了。再也不想回去写Qt/C++的属性了。)


全局单例

涛哥写了一个单独的qml文件,顶层就是一个QtObject类型,里面会有一大堆属性。
颜色、字体一类的配置都在这里。
  1. // GlobalConfig.qml
  2. import QtQuick 2.0
  3. QtObject {
  4.     property color titleBackground: "#c62f2f"   //标题栏的背景色
  5.     property color background: "#f6f6f6"        //标题栏之外的部分的背景色
  6.     property color reserverColor: "#ffffff"     //与背景色相反的对比色
  7.     property color textColor: "black"           //文本颜色
  8.     
  9. }



然后在main.qml中实例化它
  1. // main.qml
  2. Item {
  3.     width: 800
  4.     height: 600
  5.     GlobalConfig {
  6.         id: gConfig
  7.     }
  8.     ...
  9. }



Qml有个特性,子页面实例可以通过id访问父页面中的实例,读 写其属性、调用其函数。
在main.qml中实例化的对象,相当于是全局的了,它的id是可以在所有main.qml的子页面中访问到的。
(当然还有一种方式,通过qmldir指定单例,这种留着后面再说)


实现



皮肤的配置和原理

下面是TaoQuick中使用的gConfig
  1. // GlobalConfig.qml
  2. QtObject {
  3.     property color titleBackground: "#c62f2f"
  4.     property color background: "#f6f6f6"
  5.     property color reserverColor: "#ffffff"
  6.     property color textColor: "black"
  7.     property color splitColor: "gray"
  8.     property int currentTheme: 0
  9.     onCurrentThemeChanged: {
  10.         var t = themes.get(currentTheme)
  11.         titleBackground = t.titleBackground
  12.         background = t.background
  13.         textColor = t.textColor
  14.     }
  15.     readonly property ListModel themes: ListModel {
  16.         ListElement {
  17.             name: qsTr("一品红")
  18.             titleBackground: "#c62f2f"
  19.             background: "#f6f6f6"
  20.             textColor: "#5c5c5c"
  21.         }
  22.         ListElement {
  23.             name: qsTr("高冷黑")
  24.             titleBackground: "#191b1f"
  25.             background: "#222225"
  26.             textColor: "#adafb2"
  27.         }
  28.         ListElement {
  29.             name: qsTr("淑女粉")
  30.             titleBackground: "#faa0c5"
  31.             background: "#f6f6f6"
  32.             textColor: "#5c5c5c"
  33.         }
  34.         ListElement {
  35.             name: qsTr("富贵金")
  36.             titleBackground: "#fed98f"
  37.             background: "#f6f6f6"
  38.             textColor: "#5c5c5c"
  39.         }
  40.         ListElement {
  41.             name: qsTr(" 清爽绿")
  42.             titleBackground: "#58c979"
  43.             background: "#f6f6f6"
  44.             textColor: "#5c5c5c"
  45.         }
  46.         ListElement {
  47.             name: qsTr("苍穹蓝")
  48.             titleBackground: "#67c1fd"
  49.             background: "#f6f6f6"
  50.             textColor: "#5c5c5c"
  51.         }
  52.     }
  53. }



涛哥在所有的Page页面中,相关颜色设置都绑定到gConfig的相应属性上。
那么换皮肤,只需要修改gConfig中的颜色相关属性即可。因为修改属性时会触发change信号,而所有的Page都绑定了
gConfig的属性,会自动在发生change时重新读属性,修改后的颜色自动就生效了。
这里顺带说一下,主题的颜色相关属性越少越好,因为太多了不容易识别、不好维护,
文章1 Qml组件化编程1-按钮的定制与封装
中提到的Qt.lighter和Qt.darker就是一种减少颜色属性数量的神器。


皮肤选择器

再来看一下,涛哥参考 网易云音乐 做的皮肤选择器
  1. TImageBtn {     //图片按钮,参考文章1
  2.     width: 20
  3.     height: 20
  4.     anchors.verticalCenter: parent.verticalCenter
  5.     imageUrl: containsMouse ? "qrc:/Image/Window/skin_white.png" : "qrc:/Image/Window/skin_gray.png"
  6.     onClicked: {
  7.         skinBox.show()
  8.     }
  9.     TPopup {    //自定义的弹窗,带三角尖尖的那个。
  10.         id: skinBox
  11.         barColor: gConfig.reserverColor
  12.         backgroundWidth: 280
  13.         backgroundHeight: 180
  14.         contentItem: GridView {
  15.             anchors.fill: parent
  16.             anchors.margins: 10
  17.             model: gConfig.themes
  18.             cellWidth: 80
  19.             cellHeight: 80
  20.             delegate: Item {
  21.                 width: 80
  22.                 height: 80
  23.                 Rectangle { //表示主题色的色块
  24.                     anchors.fill: parent
  25.                     anchors.margins: 4
  26.                     height: width
  27.                     color: model.titleBackground
  28.                 }
  29.                 Rectangle { //主题色边框,鼠标悬浮时显示
  30.                     anchors.fill: parent
  31.                     color: "transparent"
  32.                     border.color: model.titleBackground
  33.                     border.width: 2
  34.                     visible: a.containsMouse
  35.                 }
  36.                 Text {  //主题名字
  37.                     anchors {
  38.                         left: parent.left
  39.                         bottom: parent.bottom
  40.                         leftMargin: 8
  41.                         bottomMargin: 8
  42.                     }
  43.                     color: "white"
  44.                     text: model.name
  45.                 }
  46.                 Rectangle { //右下角圆圈圈,当前选中的主题
  47.                     x: parent.width - width
  48.                     y: parent.height - height
  49.                     width: 20
  50.                     height: width
  51.                     radius: width / 2
  52.                     color: model.titleBackground
  53.                     border.width: 3
  54.                     border.color: gConfig.reserverColor
  55.                     visible: gConfig.currentTheme === index
  56.                 }
  57.                 MouseArea { //鼠标状态
  58.                     id: a
  59.                     anchors.fill: parent
  60.                     hoverEnabled: true
  61.                     onClicked: {    //切主题操作
  62.                         gConfig.currentTheme = index
  63.                     }
  64.                 }
  65.             }
  66.         }
  67.         
  68.     }
  69. }




带三角形尖尖的弹窗组件


  1. // TPopup.qml
  2. import QtQuick 2.9
  3. import QtQuick.Controls 2.5
  4. Item {
  5.     id: root
  6.     anchors.fill: parent
  7.     property alias popupVisible: popup.visible
  8.     property alias contentItem: popup.contentItem
  9.     property color barColor: "white"
  10.     property alias backgroundItem: background
  11.     property real backgroundWidth: 200
  12.     property real backgroundHeight: 160
  13.     property color borderColor:  barColor
  14.     property real borderWidth: 0
  15.     property real verticalOffset: 20
  16.     //矩形旋转45度,一半被toolTip遮住(重合),另一半三角形和ToolTip组成一个带箭头的ToolTip
  17.     Rectangle {
  18.         id: bar
  19.         visible: popup.visible
  20.         rotation: 45
  21.         width: 16
  22.         height: 16
  23.         color: barColor
  24.         //水平居中
  25.         anchors.horizontalCenter: parent.horizontalCenter
  26.         //垂直方向上,由ToolTip的y值,决定位置
  27.         anchors.verticalCenter: parent.bottom
  28.         anchors.verticalCenterOffset: verticalOffset
  29.     }
  30.     Popup {
  31.         id: popup
  32.         width: backgroundWidth
  33.         height: backgroundHeight
  34.         background: Rectangle {
  35.             id: background
  36.             color: barColor
  37.             radius: 8
  38.             border.color:borderColor
  39.             border.width: borderWidth
  40.         }
  41.     }
  42.     function show() {
  43.         popup.x = (root.width - popup.width) / 2
  44.         popup.y = root.height + verticalOffset
  45.         popupVisible = true
  46.     }
  47.     function hide() {
  48.         popupVisible = false
  49.     }
  50. }


转载声明

文章出自涛哥的博客 – 点击这里查看涛哥的博客文章采用 知识共享署名-非商业性使用-相同方式共享 4.0 国际许可协议进行许可, 转载请注明出处, 谢谢合作 © 涛哥


联系方式


作者涛哥
开发理念弘扬鲁班文化,传承工匠精神
博客https://jaredtao.github.io
知乎https://www.zhihu.com/people/wentao-jia
邮箱jared2020@163.com
微信xsd2410421
QQ759378563

请放心联系我,乐于提供咨询服务,也可洽谈商务合作相关事宜。

涛哥是个Qml高手,著有《Qml组件化编程》《Qml特效》系列教程,见知乎专栏-Qt进阶之路:https://zhuanlan.zhihu.com/TaoQt
或微信公众号:Qt进阶之路
离线dd759378563

只看该作者 1楼 发表于: 2019-05-18
代码格式已经修正
涛哥是个Qml高手,著有《Qml组件化编程》《Qml特效》系列教程,见知乎专栏-Qt进阶之路:https://zhuanlan.zhihu.com/TaoQt
或微信公众号:Qt进阶之路
离线big_mouse

只看该作者 2楼 发表于: 2020-04-15
快速回复
限100 字节
 
上一个 下一个