1.  引言

提到界面引擎,大家一定会想到“换肤”这个关键字。的确,使用界面引擎开发的产品,其换肤功能的实现会比使用系统API开发的简单很多。也有很多朋友经常问我们:“你们的界面引擎支持换肤么? ”其实在不同产品之间,换肤的需求千变万化,引擎的开发者也不可能开发出一个“万金油”型的换肤功能来,而固定的换肤功能又往往不能满足开发者的需要。

在这一点上,Bolt引擎认为:换肤功能应该被放在产品层面,而不是引擎层面;引擎则提供完成换肤功能所必要的自由度和基础功能。基于这些功能,产品可以定制自己的换肤需求,然后使用界面引擎将其方便快速地实现。

常见的换肤需求一般有以下几种:

1.  控件资源换肤(比如windows XP的主题改变),但控件的布局不变

2.  换肤同时改变整个界面布局(比如WindowsMeidaPlayer)

3.  用户可以通过自定义的图片改变产品的界面(win7的主题)

在原有Windows API下这些需求的实现,使用简单的伪代码描述如下:

1.  假设按钮控件资源换肤

LRESULT MyButton::OnDrawItem()

{

Const char* resid = ResManager.GetResidByCtrlId(m_id);

//换肤后得到不同的hBitmap

HBITMAP hBitmap = SkinManager.GetBitmapByResid(resid);

HDC dc = GetDC();

DrawBitmap(dc,hBitmap)

}

Void SkinManager::ChangeSkin(skinName)

{

ResManager.LoadFrom(skinName.zip);

UpdateAllWindowInProcess();

}

2.  改变界面布局

LRESULT MyWind::OnDrawItem()

{

PosInfo nowpos = SkinManager.GetPosByCtrlId(m_id);

MoveWindow(m_hWnd, nowpos.left, nowpos.top, nowpos.width, nowpos.height);

UpdateWindow();

}

3.  自定义图片

LRESULT MyBkg::OnDrawItem()

{

Const char* imagePath = GetBkgPath();

Int nWidth = 0, nHeight = 0;

HBITMAP hBitmap = SkinManager.LoadBitmapFromPath(imagePath, &nWidth, &nHeight);

DrawState(dc, NULL, NULL, hBitmap, 0, 0, 0, 0, 0, DST_BITMAP|DSS_NORMAL);

UpdateWindow();

}

以使用Bolt引擎开发的迅雷7为例,迅雷7的换肤功能相当强大,除了提供毛玻璃效果、部分透明度调整、任意图片做背景图片之外,还有界面元素调色方案、自动调色、字体效果更换等进阶的换肤功能,而这些功能在其他的产品中是难以见到的。总而言之,迅雷7中的换肤需求有以下几点:

1. 任意图片作为背景图片

2. 界面元素的透明度调整,可以整体调整也可以分块调整

3. 界面边缘阴影

4. 界面毛玻璃效果

5. 界面元素的统一调色

6. 字体的统一更换

引擎按照粒度的层级,将换肤的层次由低到高分为4个层次:对象层、资源层、XAR包层、逻辑层。层次越低,换肤涉及到的对象就越底层,自由度就相对越小。迅雷7的换肤功能主要集中在对象层和资源层,以不改变基本界面样式为前提。应用后两层的换肤方法,可以将换肤应用到产品的每一个角落,实现真正意义上的“随意换”。

在以下的章节中,我们会按照由低到高的层次顺序,辅以迅雷7的换肤需求,讲解不同层面上换肤效果的设计思路。

2.  初级换肤:第一层,对象层面的换肤处理

对象层面的换肤,是通过对对象树上单个元素的改变,达到换肤的效果。迅雷7换肤需求中的1、2、3三点,就属于对象层面。

产品中最大的界面元素,也是最容易被人忽略的元素,就是软件背景。不像传统基于Windows的开发,在引擎中窗口的背景也是界面对象树的一部分。由于引擎的分层设计特性,对象树必须需要一个足够大的根元素来承载上层的其他元素,此时这个“根元素”就可以作为产品的背景使用。再加上引擎可以实时更改元素的特性,这样最初级的换肤功能就实现了。

下面是一个简单实现了自定义图片背景,填充背景和毛玻璃效果的背景控件的结构,产品的布局可以建立在这个背景控件的基础之上。在这个结构中,几个Object的大小都是相同的,图上这种表示方式是为了清晰地显示出控件结构。其中,ImageObject用于放置自定义的背景图片,FillObject则是为了当背景图片不够大,或者没有背景图片时充当填充背景所用。

BkgCtrl的控件结构

通常情况下,充当背景元素的对象一般是ImageObject,TextureObject和FillObject三种。

ImageObject是位图对象,可以显示一张位图,并且有平铺、拉伸、原图三种显示方式,水平和垂直对齐方式也是可调的。迅雷7的图片背景就是使用ImageObject实现的。从文件中读取图片和图片大小等信息,可以使用XLGraphic中的对应C接口来完成。TextureObject是纹理对象,可以按照资源文件中配置的方式,显示一张符合Bolt引擎定义的纹理。若是不想放置图片,也可以用填充对象(FillObject)来充当背景。FillObject可以用单色、线性、圆形这几种填充方法,将指定的颜色填充到其中去。迅雷7的默认背景就是用FillObject做出的。

关于界面透明的实现,引擎中所有元对象都可以通过SetAlpha函数来单独地调整透明度。注意此处透明度的调整只限于该对象本身,对该对象的子对象没有影响。

另外要注意的是,界面透明,边缘阴影和Vista以后系统下毛玻璃效果的实现,都依赖于开启主窗口的layer属性。层窗口(Layered窗口)是Windows XP以后支持的窗口特性。开启了layer属性的窗口渲染时可以进行alpha混合,而没开启layer属性的窗口在绘制时则忽略alpha属性。由于系统实现上的原因,XP系统下,非layer窗口的渲染效率要比layer窗口高;Vista及以后系统则相反,layer窗口的绘制效率更高。

那么,在bolt引擎中又该如何实现毛玻璃效果呢?毛玻璃效果是通过在界面上放置BlurObject来实现的,想要毛玻璃效果生效,还需配置主窗口的blur属性为1。另外,BlurObject也属于元对象,可以任意调整大小,移动位置。因此,在界面上实现部分毛玻璃效果也是可行的。

3.  中级换肤:第二层,现有资源层面的换肤处理

现有资源层面的换肤,指对现有资源包中的资源进行处理,比如调色、更换字体等。由于一个资源可以被多个对象使用,改变一个资源就可以改变多个对象的视觉效果。

在Bolt引擎中,每一个能被加载的资源(包括位图、纹理、颜色、字体等),都有一个在本资源包中不重复的id,引擎可以根据id来对指定的资源进行处理。

能做到这一点,正是基于引擎的资源预处理(Pretreat)机制,用户可以对一类资源设置自己的预处理函数。预处理机制在渲染这类资源之前,使用者可以通过预处理函数来改变资源渲染的结果,包括调色、划线、增加文字等;具体可参见引擎参考手册。

下面是一个有关设置Pretreat函数的小例子,用Lua代码写成:

local function ModifyWith(h, s, v, bepaint)

local function Pretreat(resId, resObj)

resObj:ModifyColor(h, s, v, bepaint)

end

return Pretreat

end

local xarManager = XLGetObject("Xunlei.UIEngine.XARManager")

xarManager:SetBitmapPretreater(“mainxar::default.bitmap”, ModifyWith(h,s,v,1))

xarManager:CommitPretreater()

其中,SetBitmapPretreater函数是设置预处理函数的关键,函数原型为SetBitmapPretreater(resID, function),其中ResID为想要设置预处理函数的资源ID,例子中“mainxar::default.bitmap”代表了mainxar包中的default.bitmap资源;为处理大型产品有多个xar包的情况,SetBitmapPretreater函数是xar相关的,可以通过”xar包名::资源ID”的形式来指定某个xar包中的资源。如果不指定包名,则该函数会被设置到所有加载的xar包中。Function是一个自定义的lua函数,这个lua函数必须返回一个原型为preatret(resID, resObj)的lua函数,当被设置的资源ID被渲染时,引擎会主动调用该preatret(resID, resObj)函数,用户可以在函数内对resObj进行操作,改变渲染结果。

要注意的是,对于不同种类的资源,设置预处理的函数也是不同的,目前已经支持的有:

l  SetBitmapPretreater

l  SetColorPretreater

l  SetTexturePretreater

l  SetImageSeqPretreater

该例中,设置的预处理函数只对资源做了ModifyColor操作,即对资源进行调色。ModifyColor使用HSV颜色空间进行调色,与一般的RGB颜色空间不同,可以使用XLGraphic提供的C函数来进行RGB和HSV之间的转换。

最重要的一点:设置完成后不要忘了调用CommitPretreater来提交预处理函数的修改,否则设置的预处理函数会不起作用。

为了方便大家调色,XLGraphic提供了C函数XL_GetBitmapMainColor,用于获取位图的主色调,方便大家调色使用,达到界面一致的效果。

4.  高级换肤:第三层,xar层面的换肤处理

前两层的换肤处理,都是通过改变已有的对象来达到目的。引擎支持一个xar包中带有多个资源包,并且在运行过程中可以实时更换资源包来达到换肤的效果。

更换资源包可以调用xarManager的SelectResPackage方法,注意新包加载完成以后,之前资源包中所有的资源对象都会被释放。具体可参见开发文档。资源包可以在xar的package.cfg文件中配置。

在资源包的resource.cfg文件中,可以通过添加loadscript和unloadscript节点,自定义资源包load和unload时的脚本,给予开发者更大的自由度。

这里要注意一下,资源替换和资源预处理的区别。资源预处理只能针对已经加载完成的资源包,即只能对现有资源的基础上进行一些小的改变,改变针对的是某个特定的资源;而资源替换则是将旧有的资源包整个替换成为新的资源包,这是针对所有资源的改变。

5.  终极换肤:第四层,逻辑层面的换肤处理

到了这一层,“换肤”已经不是一个具体的功能或者函数,而是一种思想。利用Bolt引擎运行中更改对象状态的特性,“换肤”可以不局限在调整资源和颜色的范畴中,对界面元素的位置,层次甚至个数的调整也是可能的。这一层次的定制灵活性最大,要根据需求的具体情况具体分析。

具体来说,就是通过XLLuaRuntime的相关功能,在换肤的时候执行一段自定义的脚本,通过脚本来实现对布局的修改。

6. 皮肤包和皮肤管理器设计指南

上面的4个换肤层次,主要解释了引擎在换肤方面提供的功能。要将这些功能整合起来,一般会需要开发者自己实现一个“皮肤管理器”,将以上的几部分功能统合起来,真正为己所用。接下来我们就简单看一下,皮肤包和皮肤管理器所应该具有的功能。

要实现第一层级的换肤,更换背景图片、调整透明度等,皮肤包中就要有一个存储了背景图片、透明度设定等的皮肤配置文件;皮肤管理器可以从配置文件中读取各种设定,来通知界面进行改变。管理器最好还能有事件注册机制,界面Lua可以向皮肤管理器中注册事件,当设定更改之后,管理器可以通知Lua,让Lua部分作更灵活的调整。

实现第二层级的换肤,皮肤包应该有一个调色配置文件,调色配置文件包含可调色的资源id,皮肤管理器通过读取配置文件,得到可调色的资源id。当背景图片或者填充对象发生变化时,可以通过这些资源id来调用相对应的SetPretreater函数,达到配色效果。

实现第三层次的换肤效果,可以将额外的资源包放在皮肤包中,管理器通过XLFSIO的映射函数将资源包映射到资源目录中去,就可以通过SelectResPackage函数来更换资源包了。

对于第四层次,逻辑层面的换肤,皮肤包也可以包括一些lua脚本文件作为换肤脚本,皮肤管理器在加载皮肤包的时候,通过XLLuaRuntime的相关接口来执行换肤脚本,达到完全自定义的效果。

用Bolt引擎实现换肤指南相关推荐

  1. android 第三方登录界面,Android App集成第三方登录与换肤指南

    Android App集成第三方登录与换肤指南 文档编辑 概述 本文主要是介绍了如何通过开源框架快速支持QQ和微信登录,并介绍了如何实现app快速换肤 QQ登录接入 APP要支持QQ登录,需要先到腾讯 ...

  2. Android 换肤指南

    一.换肤方案 目前,市面上Android的换肤方案主要有Resource方案和AssetManager替换方案两种方案. 其中,Resource方案是用户提前自定义一些主题,然后将指定主题对应的 id ...

  3. Android-skin-support换肤框架使用指南

    1.介绍 针对Android应用的换肤需求,通过Android-skin-support框架可以很好地优化APP的代码结构,不需要将所有皮肤的图片资源和xml放在一起,通过相关接口,加载相应主题的皮肤 ...

  4. Qt - 换肤功能实现

    文章目录 前言 Qt内置风格 QPalette QSS QSS样式 一般样式 选择器 子控件 伪状态 属性 使用 分离QSS 推荐工具 Qsseditor QssStylesheetEditor QS ...

  5. Wince输入法换肤换语言机制

    需求描述: 最近由于邦健客户提出新需求:需要在一个系统内同时实现中英文两个语种的输入法. 即,客户的wince系统语言版本是中文,但在其中的应用软件有中文和英文两个版本,所以相应的输入法也需要做调整, ...

  6. opengl源码 实现无缝切换图片过场_手把手讲解 Android hook技术实现一键换肤

    前言 产品大佬又提需求啦,要求app里面的图表要实现白天黑夜模式的切换,以满足不同光线下都能保证足够的图表清晰度. 怎么办?可能解决的办法很多,你可以给图表view增加一个toggle方法,参数Str ...

  7. JXTheme:iOS9+换肤/暗黑模式最佳方案之一,轻量级、高度自定义、swift编写

    简介 2018年苹果在macOS系统引入了暗黑模式,一经推出广受好评.尤其是我们程序员,经常与代码.文本打交道,亮色风格的界面看久了,眼睛会特别累.有了暗黑模式之后,我们的眼睛终于能被温柔对待了.而且 ...

  8. 前端JavaScript(1) --Javascript简介,第一个JavaScript代码,数据类型,运算符,数据类型转换,流程控制,百度换肤,显示隐藏...

    一.Javascript简介 Web前端有三层: HTML:从语义的角度,描述页面结构 CSS:从审美的角度,描述样式(美化页面) JavaScript:从交互的角度,描述行为(提升用户体验) Jav ...

  9. 一键换肤丨酷雷曼VR全景系统皮肤高燃登场

    重磅更新,酷雷曼VR全景系统皮肤高燃登场,一键换肤,全景作品外观皮肤随意选择,想用哪款用哪款. 酷雷曼VR全景皮肤功能,为各行各业定制更适合行业属性以及行业需求的实用性功能皮肤,采用一键换肤的快捷方式 ...

  10. python 全栈开发,Day50(Javascript简介,第一个JavaScript代码,数据类型,运算符,数据类型转换,流程控制,百度换肤,显示隐藏)...

    一.Javascript简介 Web前端有三层: HTML:从语义的角度,描述页面结构 CSS:从审美的角度,描述样式(美化页面) JavaScript:从交互的角度,描述行为(提升用户体验) Jav ...

最新文章

  1. als算法参数_Spark2.0协同过滤与ALS算法介绍
  2. java 压缩多个文件_java实现一次性压缩多个文件到zip中的方法示例
  3. eclipse远程调试失败
  4. 第九章 转移指令的原理
  5. 在nodejs中创建cluster
  6. SAP CRM WebClient UI根据扩展字段搜索出结果的实现原理
  7. JavaEE Tutorials (25) - 使用Java EE拦截器
  8. mongodb的副本集总结
  9. Spring Boot + WebMagic 实现网页爬虫,写得太好了!
  10. 判断数组是否为某二叉搜索树的后序遍历
  11. 星辰数据空号检测API文档
  12. CS5216 Capstone DP to hdmi 1080p转换器或者转接线设计原理|CS5216 DP转HDMI转换电路原理图
  13. CSDN博客给我带来的一些诱惑和选择机会
  14. Python学多久才能独立接单赚钱?一个月足够了,本人私藏的学习计划分享给大家
  15. PCFG句法分析之CYK算法
  16. 关于陌陌和微信表情页与输入法之间切换的问题
  17. 在EXCEL下用VBA编程提高人事数据的管理效率
  18. 【Simulink】粒子群算法(PSO)整定PID参数(附代码和讲解)
  19. ChemDraw Professional for Mac 16.0.1.4 专业的生物化学绘图软件
  20. 视觉惯性里程计 VIO

热门文章

  1. CSDN VIP 常见问题解答
  2. 京瓷1020怎么打印自检页_惠普打印机怎样打印测试页
  3. 统计项目代码行数的工具——SLOCCount
  4. [双十二优惠大放送] 我是小册姐,我因掘金小册而被创造,谢谢你们20天的陪伴...
  5. fortran语言能用matlab,fortran语言与matlab
  6. 6天掌握记忆宫殿,你就是记忆大师
  7. 集装箱装柜计算机器在线,装箱大师在线计算教程
  8. scratch3文件转exe文件方法
  9. 北京理工大学计算机学院2021拟录取名单,北京理工大学管理与经济学院2021年硕士研究生拟录取通知...
  10. Latex笔记:IEEE Access模板 图片排版问题汇总