好吧,其实这个是之前做的项目中遇到的一个小问题。当时的情况是这样的,我们一直用qml开发app,然后头头希望我们的程序能够有换肤功能,并且在每种皮肤下,都可以替换不同颜色。也就是标题所说的“又可以换肤,每种皮肤下又可换多种颜色”。

嗯,分析下要实现的需求。

换皮肤的话,那就要求在制作页面+组件的时候要小心,尤其是组件的时候,将逻辑功能control和显示样式style进行拆分。然后把style统一放在某个文件夹下,通过一个变量存储style文件夹的地址,然后换肤的过程就是替换style文件夹地址的过程。嗯,类似如下:

项目结构

图中嘛,皮肤1和皮肤2文件夹内都有一套对应组件的style文件。然后替换不同的地址,造成了组件样式的变化。同时,因为把所有逻辑功能等放在了control文件里,所以样式style文件中,只有外观的定义修改起来也相对简单,并且接口什么的不会因为样式的改变而改变。

再说说换颜色,这个很好实现,我们原来在编写代码的时候,颜色值都是硬编码在代码中的,那么只要把之前用到的颜色值进行归类,然后替换成全局的颜色变量就好了。

不过现在的问题就是,将两者组合起来后,因为不同的皮肤,对于颜色的多少啊,位置啥的定义都不相同,所以提取出来的颜色该以什么结构放在项目中。为了解决这个问题,我在每个皮肤文件夹内都有一个调色板文件,然后在全局有个取色器文件。然后根据当前所选的皮肤来进行取色。嗯,大概的结构如下:

带调色板的项目结构

大概就是这样的一个结构,不过在具体实现的时候当然没有就这样把取色器代码放在根上,这样多乱啊=3=

代码如下:

//ColorPalette.qml 即上文所说的取色器
pragma Singleton
import QtQuick 2.4

Item {
    id:colorRoot
    
    property string __colorType: "blue" //主题颜色
    
    property string __colorComponentUrl: "."+"/ColorTable.qml" //主题URL
    
    property Component __colorComponent: null
    
    property var __colorTable
    
    function initComponent() {
        if (__colorComponent === null) {
            colorRoot.__colorComponent = Qt.createComponent(colorRoot.__colorComponentUrl)
            if (colorRoot.__colorComponent.status === Component.Ready) {
                colorRoot.__colorTable = colorRoot.__colorComponent.createObject(colorRoot);
            }
        }
    }

    function color(type) {
        colorRoot.initComponent()
        try {
            if (colorRoot.__colorTable[colorRoot.__colorType].hasOwnProperty(type)){
                return colorRoot.__colorTable[colorRoot.__colorType][type]
            }else{
                return "#000000"
            }
        }catch (err) {
            return "#000000"
        }
    }
}

下面是每个皮肤里的调色板示例:

//ColorTable.qml 即调色板
import QtQuick 2.4

QtObject {

    readonly property var blue: {
        1:"#cee9f9",
        2:"#90bcd6",
        3:"#c0d0df",
        4:"#ffffff",
        5:"#57a2d8",
        6:"#007aff",
        7:"#aae0ff",
        8:"#227bdd",
        9:"#3a6ca9",
        10:"#7099bf",
    }

    readonly property var red: {
        1:"#ffe3e3",
        2:"#ffa3a3",
        3:"#f5d0d1",
        4:"#ffffff",
        5:"#57a2d8",
        6:"#007aff",
        7:"#aae0ff",
        8:"#227bdd",
        9:"#ffbcc0",
        10:"#eb7777",
    }
}

可以看到在这个皮肤的调色板中,一共定义了2种色调,一个叫做blue,一个叫做red,然后每种色调里面都用到了10种颜色。假设在组件里的某个颜色你需要用9号颜色,可以类似ColorPalette.color(9)这么写。

最后要注意的事,由于当时项目不要求有动态换肤,或者说是用户自己在程序里换肤这个功能,所以你看到在上面的ColorPalette.qml代码中,创建调色板的Component只有一次,而没有监控__colorComponentUrl里的主题url的变化,当它变化的时候重新createComponent

嗯,酱油结束,继续干活去,嘿!( ̄▽ ̄)"